mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-31 15:22:16 +00:00
Payment channels: protocol tweak - when the client sends a CLOSE, the server sends a CLOSE back that contains the final negotiated contract, so it can be inserted into the wallet without needing to wait for a network broadcast (this is useful if the client does not have internet connectivity at that point).
This commit is contained in:
parent
b9aa1e4d1e
commit
6342af0913
@ -91,7 +91,9 @@ public class PaymentChannelClient {
|
||||
WAITING_FOR_INITIATE,
|
||||
WAITING_FOR_REFUND_RETURN,
|
||||
WAITING_FOR_CHANNEL_OPEN,
|
||||
CHANNEL_OPEN
|
||||
CHANNEL_OPEN,
|
||||
WAITING_FOR_CHANNEL_CLOSE,
|
||||
CHANNEL_CLOSED,
|
||||
}
|
||||
@GuardedBy("lock") private InitStep step = InitStep.WAITING_FOR_CONNECTION_OPEN;
|
||||
|
||||
@ -253,7 +255,7 @@ public class PaymentChannelClient {
|
||||
receiveChannelOpen();
|
||||
return;
|
||||
case CLOSE:
|
||||
conn.destroyConnection(CloseReason.SERVER_REQUESTED_CLOSE);
|
||||
receiveClose(msg);
|
||||
return;
|
||||
case ERROR:
|
||||
checkState(msg.hasError());
|
||||
@ -290,6 +292,26 @@ public class PaymentChannelClient {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("lock")
|
||||
private void receiveClose(Protos.TwoWayChannelMessage msg) throws VerificationException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
if (msg.hasClose()) {
|
||||
Transaction closeTx = new Transaction(wallet.getParams(), msg.getClose().getTx().toByteArray());
|
||||
// TODO: set source
|
||||
if (state != null && state().isCloseTransaction(closeTx)) {
|
||||
// The wallet has a listener on it that the state object will use to do the right thing at this
|
||||
// point (like watching it for confirmations). The tx has been checked by now for syntactical validity
|
||||
// and that it correctly spends the multisig contract.
|
||||
wallet.receivePending(closeTx, null);
|
||||
}
|
||||
}
|
||||
if (step == InitStep.WAITING_FOR_CHANNEL_CLOSE)
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
else
|
||||
conn.destroyConnection(CloseReason.SERVER_REQUESTED_CLOSE);
|
||||
step = InitStep.CHANNEL_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Called when the connection terminates. Notifies the {@link StoredClientChannel} object that we can attempt to
|
||||
* resume this channel in the future and stops generating messages for the server.</p>
|
||||
@ -327,10 +349,11 @@ public class PaymentChannelClient {
|
||||
lock.lock();
|
||||
try {
|
||||
checkState(connectionOpen);
|
||||
step = InitStep.WAITING_FOR_CHANNEL_CLOSE;
|
||||
log.info("Sending a CLOSE message to the server and waiting for response indicating successful propagation.");
|
||||
conn.sendToServer(Protos.TwoWayChannelMessage.newBuilder()
|
||||
.setType(Protos.TwoWayChannelMessage.MessageType.CLOSE)
|
||||
.build());
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
@ -123,8 +123,12 @@ public class PaymentChannelClientState {
|
||||
initWalletListeners();
|
||||
}
|
||||
|
||||
private boolean isCloseTransaction(Transaction tx) {
|
||||
/**
|
||||
* Returns true if the tx is a valid close transaction.
|
||||
*/
|
||||
public boolean isCloseTransaction(Transaction tx) {
|
||||
try {
|
||||
tx.verify();
|
||||
tx.getInput(0).verify(multisigContract.getOutput(0));
|
||||
return true;
|
||||
} catch (VerificationException e) {
|
||||
|
@ -3,6 +3,8 @@ package com.google.bitcoin.protocols.channels;
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.protocols.channels.PaymentChannelCloseException.CloseReason;
|
||||
import com.google.bitcoin.utils.Threading;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.protobuf.ByteString;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import org.bitcoin.paymentchannel.Protos;
|
||||
@ -304,9 +306,31 @@ public class PaymentChannelServer {
|
||||
case CLOSE:
|
||||
log.info("Got CLOSE message, closing channel");
|
||||
connectionClosing = true;
|
||||
if (state != null)
|
||||
state.close();
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
if (state != null) {
|
||||
Futures.addCallback(state.close(), new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction result) {
|
||||
// Send the successfully accepted transaction back to the client.
|
||||
final Protos.TwoWayChannelMessage.Builder msg = Protos.TwoWayChannelMessage.newBuilder();
|
||||
msg.setType(Protos.TwoWayChannelMessage.MessageType.CLOSE);
|
||||
if (result != null) {
|
||||
// Result can be null on various error paths, like if we never actually opened
|
||||
// properly and so on.
|
||||
msg.getCloseBuilder().setTx(ByteString.copyFrom(result.bitcoinSerialize()));
|
||||
}
|
||||
conn.sendToClient(msg.build());
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
log.error("Failed to broadcast close TX", t);
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
|
||||
}
|
||||
return;
|
||||
case ERROR:
|
||||
checkState(msg.hasError());
|
||||
@ -407,9 +431,9 @@ public class PaymentChannelServer {
|
||||
lock.lock();
|
||||
try {
|
||||
if (connectionOpen && !connectionClosing) {
|
||||
conn.sendToClient(Protos.TwoWayChannelMessage.newBuilder()
|
||||
.setType(Protos.TwoWayChannelMessage.MessageType.CLOSE)
|
||||
.build());
|
||||
final Protos.TwoWayChannelMessage.Builder msg = Protos.TwoWayChannelMessage.newBuilder();
|
||||
msg.setType(Protos.TwoWayChannelMessage.MessageType.CLOSE);
|
||||
conn.sendToClient(msg.build());
|
||||
conn.destroyConnection(CloseReason.SERVER_REQUESTED_CLOSE);
|
||||
}
|
||||
} finally {
|
||||
|
@ -344,7 +344,7 @@ public class PaymentChannelServerState {
|
||||
tx.getInput(0).setScriptSig(scriptSig);
|
||||
}
|
||||
|
||||
final SettableFuture<PaymentChannelServerState> closedFuture = SettableFuture.create();
|
||||
final SettableFuture<Transaction> closedFuture = SettableFuture.create();
|
||||
/**
|
||||
* <p>Closes this channel and broadcasts the highest value payment transaction on the network.</p>
|
||||
*
|
||||
@ -355,11 +355,12 @@ public class PaymentChannelServerState {
|
||||
* simply set the state to {@link State#CLOSED} and let the client handle getting its refund transaction confirmed.
|
||||
* </p>
|
||||
*
|
||||
* @return a future which completes when the provided multisig contract successfully broadcasts, or throws if the broadcast fails for some reason
|
||||
* Note that if the network simply rejects the transaction, this future will never complete, a timeout should be used.
|
||||
* @throws ValueOutOfRangeException If the payment transaction would have cost more in fees to spend than it was worth
|
||||
* @return a future which completes when the provided multisig contract successfully broadcasts, or throws if the
|
||||
* broadcast fails for some reason. Note that if the network simply rejects the transaction, this future
|
||||
* will never complete, a timeout should be used.
|
||||
* @throws ValueOutOfRangeException If the payment tx would have cost more in fees to spend than it is worth.
|
||||
*/
|
||||
public synchronized ListenableFuture<PaymentChannelServerState> close() throws ValueOutOfRangeException {
|
||||
public synchronized ListenableFuture<Transaction> close() throws ValueOutOfRangeException {
|
||||
if (storedServerChannel != null) {
|
||||
StoredServerChannel temp = storedServerChannel;
|
||||
storedServerChannel = null;
|
||||
@ -371,11 +372,13 @@ public class PaymentChannelServerState {
|
||||
}
|
||||
|
||||
if (state.ordinal() < State.READY.ordinal()) {
|
||||
log.error("Attempt to close channel in state " + state);
|
||||
state = State.CLOSED;
|
||||
closedFuture.set(this);
|
||||
closedFuture.set(null);
|
||||
return closedFuture;
|
||||
}
|
||||
if (state != State.READY) {
|
||||
// TODO: What is this codepath for?
|
||||
log.warn("Failed attempt to close a channel in state " + state);
|
||||
return closedFuture;
|
||||
}
|
||||
@ -386,7 +389,7 @@ public class PaymentChannelServerState {
|
||||
// the submission of an initial zero-valued payment during the open phase.
|
||||
log.warn("Closing channel that never received any payments.");
|
||||
state = State.CLOSED;
|
||||
closedFuture.set(this);
|
||||
closedFuture.set(null);
|
||||
return closedFuture;
|
||||
}
|
||||
Transaction tx = null;
|
||||
@ -426,7 +429,7 @@ public class PaymentChannelServerState {
|
||||
@Override public void onSuccess(Transaction transaction) {
|
||||
log.info("TX {} propagated, channel successfully closed.", transaction.getHash());
|
||||
state = State.CLOSED;
|
||||
closedFuture.set(PaymentChannelServerState.this);
|
||||
closedFuture.set(transaction);
|
||||
}
|
||||
|
||||
@Override public void onFailure(Throwable throwable) {
|
||||
|
@ -83,7 +83,6 @@ public class StoredPaymentChannelServerStates implements WalletExtension {
|
||||
synchronized (channel) {
|
||||
channel.closeConnectedHandler();
|
||||
try {
|
||||
//TODO add event listener to PaymentChannelServerStateManager
|
||||
channel.getOrCreateState(wallet, broadcaster).close();
|
||||
} catch (ValueOutOfRangeException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -143,6 +143,20 @@ public final class Protos {
|
||||
*/
|
||||
org.bitcoin.paymentchannel.Protos.UpdatePaymentOrBuilder getUpdatePaymentOrBuilder();
|
||||
|
||||
// optional .paymentchannels.Close close = 9;
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
boolean hasClose();
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
org.bitcoin.paymentchannel.Protos.Close getClose();
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
org.bitcoin.paymentchannel.Protos.CloseOrBuilder getCloseOrBuilder();
|
||||
|
||||
// optional .paymentchannels.Error error = 10;
|
||||
/**
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
@ -316,9 +330,22 @@ public final class Protos {
|
||||
bitField0_ |= 0x00000080;
|
||||
break;
|
||||
}
|
||||
case 74: {
|
||||
org.bitcoin.paymentchannel.Protos.Close.Builder subBuilder = null;
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100)) {
|
||||
subBuilder = close_.toBuilder();
|
||||
}
|
||||
close_ = input.readMessage(org.bitcoin.paymentchannel.Protos.Close.PARSER, extensionRegistry);
|
||||
if (subBuilder != null) {
|
||||
subBuilder.mergeFrom(close_);
|
||||
close_ = subBuilder.buildPartial();
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
break;
|
||||
}
|
||||
case 82: {
|
||||
org.bitcoin.paymentchannel.Protos.Error.Builder subBuilder = null;
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100)) {
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
subBuilder = error_.toBuilder();
|
||||
}
|
||||
error_ = input.readMessage(org.bitcoin.paymentchannel.Protos.Error.PARSER, extensionRegistry);
|
||||
@ -326,7 +353,7 @@ public final class Protos {
|
||||
subBuilder.mergeFrom(error_);
|
||||
error_ = subBuilder.buildPartial();
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
bitField0_ |= 0x00000200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -419,10 +446,15 @@ public final class Protos {
|
||||
* <code>CLOSE = 9;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Note that there are no optional fields set for CLOSE, it is sent by either party to
|
||||
* indicate that the channel is now closed and no further updates can happen. After this,
|
||||
* the secondary takes the most recent signature it received in an UPDATE_PAYMENT and
|
||||
* uses it to create a valid transaction, which it then broadcasts on the network.
|
||||
* Either side can send this message. If the client sends it to the server, then the server
|
||||
* takes the most recent signature it received in an UPDATE_PAYMENT and uses it to create a
|
||||
* valid transaction, which it then broadcasts on the network.
|
||||
*
|
||||
* If the server sends it to the client, then it can either be a simple "good bye" message or
|
||||
* it can include the fully signed close transaction and give it back so the client can
|
||||
* eventually broadcast it as well, in case the server fails to do so for some reason (and
|
||||
* more usefully so the client can build on it). In that case the server replies to a CLOSE
|
||||
* with another CLOSE and then disconnects.
|
||||
* </pre>
|
||||
*/
|
||||
CLOSE(8, 9),
|
||||
@ -432,7 +464,8 @@ public final class Protos {
|
||||
* <pre>
|
||||
* Used to indicate an error condition.
|
||||
* Both parties should make an effort to send either an ERROR or a CLOSE immediately
|
||||
* before closing the socket (unless they just received an ERROR or a CLOSE)
|
||||
* before closing the socket (unless they just received an ERROR or a CLOSE). This is important
|
||||
* because the protocol may not run over TCP.
|
||||
* </pre>
|
||||
*/
|
||||
ERROR(9, 10),
|
||||
@ -484,10 +517,15 @@ public final class Protos {
|
||||
* <code>CLOSE = 9;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Note that there are no optional fields set for CLOSE, it is sent by either party to
|
||||
* indicate that the channel is now closed and no further updates can happen. After this,
|
||||
* the secondary takes the most recent signature it received in an UPDATE_PAYMENT and
|
||||
* uses it to create a valid transaction, which it then broadcasts on the network.
|
||||
* Either side can send this message. If the client sends it to the server, then the server
|
||||
* takes the most recent signature it received in an UPDATE_PAYMENT and uses it to create a
|
||||
* valid transaction, which it then broadcasts on the network.
|
||||
*
|
||||
* If the server sends it to the client, then it can either be a simple "good bye" message or
|
||||
* it can include the fully signed close transaction and give it back so the client can
|
||||
* eventually broadcast it as well, in case the server fails to do so for some reason (and
|
||||
* more usefully so the client can build on it). In that case the server replies to a CLOSE
|
||||
* with another CLOSE and then disconnects.
|
||||
* </pre>
|
||||
*/
|
||||
public static final int CLOSE_VALUE = 9;
|
||||
@ -497,7 +535,8 @@ public final class Protos {
|
||||
* <pre>
|
||||
* Used to indicate an error condition.
|
||||
* Both parties should make an effort to send either an ERROR or a CLOSE immediately
|
||||
* before closing the socket (unless they just received an ERROR or a CLOSE)
|
||||
* before closing the socket (unless they just received an ERROR or a CLOSE). This is important
|
||||
* because the protocol may not run over TCP.
|
||||
* </pre>
|
||||
*/
|
||||
public static final int ERROR_VALUE = 10;
|
||||
@ -763,6 +802,28 @@ public final class Protos {
|
||||
return updatePayment_;
|
||||
}
|
||||
|
||||
// optional .paymentchannels.Close close = 9;
|
||||
public static final int CLOSE_FIELD_NUMBER = 9;
|
||||
private org.bitcoin.paymentchannel.Protos.Close close_;
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public boolean hasClose() {
|
||||
return ((bitField0_ & 0x00000100) == 0x00000100);
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.Close getClose() {
|
||||
return close_;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.CloseOrBuilder getCloseOrBuilder() {
|
||||
return close_;
|
||||
}
|
||||
|
||||
// optional .paymentchannels.Error error = 10;
|
||||
public static final int ERROR_FIELD_NUMBER = 10;
|
||||
private org.bitcoin.paymentchannel.Protos.Error error_;
|
||||
@ -770,7 +831,7 @@ public final class Protos {
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return ((bitField0_ & 0x00000100) == 0x00000100);
|
||||
return ((bitField0_ & 0x00000200) == 0x00000200);
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
@ -794,6 +855,7 @@ public final class Protos {
|
||||
returnRefund_ = org.bitcoin.paymentchannel.Protos.ReturnRefund.getDefaultInstance();
|
||||
provideContract_ = org.bitcoin.paymentchannel.Protos.ProvideContract.getDefaultInstance();
|
||||
updatePayment_ = org.bitcoin.paymentchannel.Protos.UpdatePayment.getDefaultInstance();
|
||||
close_ = org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance();
|
||||
error_ = org.bitcoin.paymentchannel.Protos.Error.getDefaultInstance();
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
@ -847,6 +909,12 @@ public final class Protos {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (hasClose()) {
|
||||
if (!getClose().isInitialized()) {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
@ -879,6 +947,9 @@ public final class Protos {
|
||||
output.writeMessage(8, updatePayment_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100)) {
|
||||
output.writeMessage(9, close_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
output.writeMessage(10, error_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
@ -923,6 +994,10 @@ public final class Protos {
|
||||
.computeMessageSize(8, updatePayment_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(9, close_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(10, error_);
|
||||
}
|
||||
@ -1047,6 +1122,7 @@ public final class Protos {
|
||||
getReturnRefundFieldBuilder();
|
||||
getProvideContractFieldBuilder();
|
||||
getUpdatePaymentFieldBuilder();
|
||||
getCloseFieldBuilder();
|
||||
getErrorFieldBuilder();
|
||||
}
|
||||
}
|
||||
@ -1100,12 +1176,18 @@ public final class Protos {
|
||||
updatePaymentBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000080);
|
||||
if (closeBuilder_ == null) {
|
||||
close_ = org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance();
|
||||
} else {
|
||||
closeBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000100);
|
||||
if (errorBuilder_ == null) {
|
||||
error_ = org.bitcoin.paymentchannel.Protos.Error.getDefaultInstance();
|
||||
} else {
|
||||
errorBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000100);
|
||||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1197,6 +1279,14 @@ public final class Protos {
|
||||
if (((from_bitField0_ & 0x00000100) == 0x00000100)) {
|
||||
to_bitField0_ |= 0x00000100;
|
||||
}
|
||||
if (closeBuilder_ == null) {
|
||||
result.close_ = close_;
|
||||
} else {
|
||||
result.close_ = closeBuilder_.build();
|
||||
}
|
||||
if (((from_bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
to_bitField0_ |= 0x00000200;
|
||||
}
|
||||
if (errorBuilder_ == null) {
|
||||
result.error_ = error_;
|
||||
} else {
|
||||
@ -1242,6 +1332,9 @@ public final class Protos {
|
||||
if (other.hasUpdatePayment()) {
|
||||
mergeUpdatePayment(other.getUpdatePayment());
|
||||
}
|
||||
if (other.hasClose()) {
|
||||
mergeClose(other.getClose());
|
||||
}
|
||||
if (other.hasError()) {
|
||||
mergeError(other.getError());
|
||||
}
|
||||
@ -1296,6 +1389,12 @@ public final class Protos {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (hasClose()) {
|
||||
if (!getClose().isInitialized()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2233,6 +2332,123 @@ public final class Protos {
|
||||
return updatePaymentBuilder_;
|
||||
}
|
||||
|
||||
// optional .paymentchannels.Close close = 9;
|
||||
private org.bitcoin.paymentchannel.Protos.Close close_ = org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance();
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.bitcoin.paymentchannel.Protos.Close, org.bitcoin.paymentchannel.Protos.Close.Builder, org.bitcoin.paymentchannel.Protos.CloseOrBuilder> closeBuilder_;
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public boolean hasClose() {
|
||||
return ((bitField0_ & 0x00000100) == 0x00000100);
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.Close getClose() {
|
||||
if (closeBuilder_ == null) {
|
||||
return close_;
|
||||
} else {
|
||||
return closeBuilder_.getMessage();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public Builder setClose(org.bitcoin.paymentchannel.Protos.Close value) {
|
||||
if (closeBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
close_ = value;
|
||||
onChanged();
|
||||
} else {
|
||||
closeBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public Builder setClose(
|
||||
org.bitcoin.paymentchannel.Protos.Close.Builder builderForValue) {
|
||||
if (closeBuilder_ == null) {
|
||||
close_ = builderForValue.build();
|
||||
onChanged();
|
||||
} else {
|
||||
closeBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public Builder mergeClose(org.bitcoin.paymentchannel.Protos.Close value) {
|
||||
if (closeBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100) &&
|
||||
close_ != org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance()) {
|
||||
close_ =
|
||||
org.bitcoin.paymentchannel.Protos.Close.newBuilder(close_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
close_ = value;
|
||||
}
|
||||
onChanged();
|
||||
} else {
|
||||
closeBuilder_.mergeFrom(value);
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public Builder clearClose() {
|
||||
if (closeBuilder_ == null) {
|
||||
close_ = org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance();
|
||||
onChanged();
|
||||
} else {
|
||||
closeBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000100);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.Close.Builder getCloseBuilder() {
|
||||
bitField0_ |= 0x00000100;
|
||||
onChanged();
|
||||
return getCloseFieldBuilder().getBuilder();
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.CloseOrBuilder getCloseOrBuilder() {
|
||||
if (closeBuilder_ != null) {
|
||||
return closeBuilder_.getMessageOrBuilder();
|
||||
} else {
|
||||
return close_;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Close close = 9;</code>
|
||||
*/
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.bitcoin.paymentchannel.Protos.Close, org.bitcoin.paymentchannel.Protos.Close.Builder, org.bitcoin.paymentchannel.Protos.CloseOrBuilder>
|
||||
getCloseFieldBuilder() {
|
||||
if (closeBuilder_ == null) {
|
||||
closeBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||
org.bitcoin.paymentchannel.Protos.Close, org.bitcoin.paymentchannel.Protos.Close.Builder, org.bitcoin.paymentchannel.Protos.CloseOrBuilder>(
|
||||
close_,
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
close_ = null;
|
||||
}
|
||||
return closeBuilder_;
|
||||
}
|
||||
|
||||
// optional .paymentchannels.Error error = 10;
|
||||
private org.bitcoin.paymentchannel.Protos.Error error_ = org.bitcoin.paymentchannel.Protos.Error.getDefaultInstance();
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
@ -2241,7 +2457,7 @@ public final class Protos {
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return ((bitField0_ & 0x00000100) == 0x00000100);
|
||||
return ((bitField0_ & 0x00000200) == 0x00000200);
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
@ -2266,7 +2482,7 @@ public final class Protos {
|
||||
} else {
|
||||
errorBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
bitField0_ |= 0x00000200;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
@ -2280,7 +2496,7 @@ public final class Protos {
|
||||
} else {
|
||||
errorBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
bitField0_ |= 0x00000200;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
@ -2288,7 +2504,7 @@ public final class Protos {
|
||||
*/
|
||||
public Builder mergeError(org.bitcoin.paymentchannel.Protos.Error value) {
|
||||
if (errorBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000100) == 0x00000100) &&
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200) &&
|
||||
error_ != org.bitcoin.paymentchannel.Protos.Error.getDefaultInstance()) {
|
||||
error_ =
|
||||
org.bitcoin.paymentchannel.Protos.Error.newBuilder(error_).mergeFrom(value).buildPartial();
|
||||
@ -2299,7 +2515,7 @@ public final class Protos {
|
||||
} else {
|
||||
errorBuilder_.mergeFrom(value);
|
||||
}
|
||||
bitField0_ |= 0x00000100;
|
||||
bitField0_ |= 0x00000200;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
@ -2312,14 +2528,14 @@ public final class Protos {
|
||||
} else {
|
||||
errorBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000100);
|
||||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional .paymentchannels.Error error = 10;</code>
|
||||
*/
|
||||
public org.bitcoin.paymentchannel.Protos.Error.Builder getErrorBuilder() {
|
||||
bitField0_ |= 0x00000100;
|
||||
bitField0_ |= 0x00000200;
|
||||
onChanged();
|
||||
return getErrorFieldBuilder().getBuilder();
|
||||
}
|
||||
@ -6447,6 +6663,453 @@ public final class Protos {
|
||||
// @@protoc_insertion_point(class_scope:paymentchannels.UpdatePayment)
|
||||
}
|
||||
|
||||
public interface CloseOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
// required bytes tx = 3;
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
boolean hasTx();
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
com.google.protobuf.ByteString getTx();
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code paymentchannels.Close}
|
||||
*/
|
||||
public static final class Close extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
implements CloseOrBuilder {
|
||||
// Use Close.newBuilder() to construct.
|
||||
private Close(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
|
||||
super(builder);
|
||||
this.unknownFields = builder.getUnknownFields();
|
||||
}
|
||||
private Close(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
|
||||
|
||||
private static final Close defaultInstance;
|
||||
public static Close getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public Close getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
private final com.google.protobuf.UnknownFieldSet unknownFields;
|
||||
@java.lang.Override
|
||||
public final com.google.protobuf.UnknownFieldSet
|
||||
getUnknownFields() {
|
||||
return this.unknownFields;
|
||||
}
|
||||
private Close(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
initFields();
|
||||
int mutable_bitField0_ = 0;
|
||||
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
|
||||
com.google.protobuf.UnknownFieldSet.newBuilder();
|
||||
try {
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
done = true;
|
||||
break;
|
||||
default: {
|
||||
if (!parseUnknownField(input, unknownFields,
|
||||
extensionRegistry, tag)) {
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 26: {
|
||||
bitField0_ |= 0x00000001;
|
||||
tx_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(this);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new com.google.protobuf.InvalidProtocolBufferException(
|
||||
e.getMessage()).setUnfinishedMessage(this);
|
||||
} finally {
|
||||
this.unknownFields = unknownFields.build();
|
||||
makeExtensionsImmutable();
|
||||
}
|
||||
}
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.bitcoin.paymentchannel.Protos.internal_static_paymentchannels_Close_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.bitcoin.paymentchannel.Protos.internal_static_paymentchannels_Close_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
org.bitcoin.paymentchannel.Protos.Close.class, org.bitcoin.paymentchannel.Protos.Close.Builder.class);
|
||||
}
|
||||
|
||||
public static com.google.protobuf.Parser<Close> PARSER =
|
||||
new com.google.protobuf.AbstractParser<Close>() {
|
||||
public Close parsePartialFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return new Close(input, extensionRegistry);
|
||||
}
|
||||
};
|
||||
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.Parser<Close> getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
// required bytes tx = 3;
|
||||
public static final int TX_FIELD_NUMBER = 3;
|
||||
private com.google.protobuf.ByteString tx_;
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasTx() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getTx() {
|
||||
return tx_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
tx_ = com.google.protobuf.ByteString.EMPTY;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
if (isInitialized != -1) return isInitialized == 1;
|
||||
|
||||
if (!hasTx()) {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
getSerializedSize();
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
output.writeBytes(3, tx_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
private int memoizedSerializedSize = -1;
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSerializedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(3, tx_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
@java.lang.Override
|
||||
protected java.lang.Object writeReplace()
|
||||
throws java.io.ObjectStreamException {
|
||||
return super.writeReplace();
|
||||
}
|
||||
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input, extensionRegistry);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseDelimitedFrom(input);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseDelimitedFrom(input, extensionRegistry);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input);
|
||||
}
|
||||
public static org.bitcoin.paymentchannel.Protos.Close parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return PARSER.parseFrom(input, extensionRegistry);
|
||||
}
|
||||
|
||||
public static Builder newBuilder() { return Builder.create(); }
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder(org.bitcoin.paymentchannel.Protos.Close prototype) {
|
||||
return newBuilder().mergeFrom(prototype);
|
||||
}
|
||||
public Builder toBuilder() { return newBuilder(this); }
|
||||
|
||||
@java.lang.Override
|
||||
protected Builder newBuilderForType(
|
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
|
||||
Builder builder = new Builder(parent);
|
||||
return builder;
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code paymentchannels.Close}
|
||||
*/
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessage.Builder<Builder>
|
||||
implements org.bitcoin.paymentchannel.Protos.CloseOrBuilder {
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.bitcoin.paymentchannel.Protos.internal_static_paymentchannels_Close_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.bitcoin.paymentchannel.Protos.internal_static_paymentchannels_Close_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
org.bitcoin.paymentchannel.Protos.Close.class, org.bitcoin.paymentchannel.Protos.Close.Builder.class);
|
||||
}
|
||||
|
||||
// Construct using org.bitcoin.paymentchannel.Protos.Close.newBuilder()
|
||||
private Builder() {
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
private Builder(
|
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
private void maybeForceBuilderInitialization() {
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
}
|
||||
}
|
||||
private static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
super.clear();
|
||||
tx_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(buildPartial());
|
||||
}
|
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptorForType() {
|
||||
return org.bitcoin.paymentchannel.Protos.internal_static_paymentchannels_Close_descriptor;
|
||||
}
|
||||
|
||||
public org.bitcoin.paymentchannel.Protos.Close getDefaultInstanceForType() {
|
||||
return org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance();
|
||||
}
|
||||
|
||||
public org.bitcoin.paymentchannel.Protos.Close build() {
|
||||
org.bitcoin.paymentchannel.Protos.Close result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public org.bitcoin.paymentchannel.Protos.Close buildPartial() {
|
||||
org.bitcoin.paymentchannel.Protos.Close result = new org.bitcoin.paymentchannel.Protos.Close(this);
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
result.tx_ = tx_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
if (other instanceof org.bitcoin.paymentchannel.Protos.Close) {
|
||||
return mergeFrom((org.bitcoin.paymentchannel.Protos.Close)other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(org.bitcoin.paymentchannel.Protos.Close other) {
|
||||
if (other == org.bitcoin.paymentchannel.Protos.Close.getDefaultInstance()) return this;
|
||||
if (other.hasTx()) {
|
||||
setTx(other.getTx());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
|
||||
public final boolean isInitialized() {
|
||||
if (!hasTx()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
org.bitcoin.paymentchannel.Protos.Close parsedMessage = null;
|
||||
try {
|
||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
parsedMessage = (org.bitcoin.paymentchannel.Protos.Close) e.getUnfinishedMessage();
|
||||
throw e;
|
||||
} finally {
|
||||
if (parsedMessage != null) {
|
||||
mergeFrom(parsedMessage);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
private int bitField0_;
|
||||
|
||||
// required bytes tx = 3;
|
||||
private com.google.protobuf.ByteString tx_ = com.google.protobuf.ByteString.EMPTY;
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasTx() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getTx() {
|
||||
return tx_;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public Builder setTx(com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
tx_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes tx = 3;</code>
|
||||
*
|
||||
* <pre>
|
||||
* A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
* the transaction is correct and then commit it to their wallet.
|
||||
* </pre>
|
||||
*/
|
||||
public Builder clearTx() {
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
tx_ = getDefaultInstance().getTx();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:paymentchannels.Close)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new Close(true);
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:paymentchannels.Close)
|
||||
}
|
||||
|
||||
public interface ErrorOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
@ -7285,6 +7948,11 @@ public final class Protos {
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_paymentchannels_UpdatePayment_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_paymentchannels_Close_descriptor;
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_paymentchannels_Close_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_paymentchannels_Error_descriptor;
|
||||
private static
|
||||
@ -7300,7 +7968,7 @@ public final class Protos {
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\024paymentchannel.proto\022\017paymentchannels\"" +
|
||||
"\274\005\n\024TwoWayChannelMessage\022?\n\004type\030\001 \002(\01621" +
|
||||
"\343\005\n\024TwoWayChannelMessage\022?\n\004type\030\001 \002(\01621" +
|
||||
".paymentchannels.TwoWayChannelMessage.Me" +
|
||||
"ssageType\0226\n\016client_version\030\002 \001(\0132\036.paym" +
|
||||
"entchannels.ClientVersion\0226\n\016server_vers" +
|
||||
@ -7311,31 +7979,32 @@ public final class Protos {
|
||||
"\006 \001(\0132\035.paymentchannels.ReturnRefund\022:\n\020",
|
||||
"provide_contract\030\007 \001(\0132 .paymentchannels" +
|
||||
".ProvideContract\0226\n\016update_payment\030\010 \001(\013" +
|
||||
"2\036.paymentchannels.UpdatePayment\022%\n\005erro" +
|
||||
"r\030\n \001(\0132\026.paymentchannels.Error\"\274\001\n\013Mess" +
|
||||
"ageType\022\022\n\016CLIENT_VERSION\020\001\022\022\n\016SERVER_VE" +
|
||||
"RSION\020\002\022\014\n\010INITIATE\020\003\022\022\n\016PROVIDE_REFUND\020" +
|
||||
"\004\022\021\n\rRETURN_REFUND\020\005\022\024\n\020PROVIDE_CONTRACT" +
|
||||
"\020\006\022\020\n\014CHANNEL_OPEN\020\007\022\022\n\016UPDATE_PAYMENT\020\010" +
|
||||
"\022\t\n\005CLOSE\020\t\022\t\n\005ERROR\020\n\"X\n\rClientVersion\022" +
|
||||
"\r\n\005major\030\001 \002(\005\022\020\n\005minor\030\002 \001(\005:\0010\022&\n\036prev",
|
||||
"ious_channel_contract_hash\030\003 \001(\014\"0\n\rServ" +
|
||||
"erVersion\022\r\n\005major\030\001 \002(\005\022\020\n\005minor\030\002 \001(\005:" +
|
||||
"\0010\"]\n\010Initiate\022\024\n\014multisig_key\030\001 \002(\014\022!\n\031" +
|
||||
"min_accepted_channel_size\030\002 \002(\004\022\030\n\020expir" +
|
||||
"e_time_secs\030\003 \002(\004\"1\n\rProvideRefund\022\024\n\014mu" +
|
||||
"ltisig_key\030\001 \002(\014\022\n\n\002tx\030\002 \002(\014\"!\n\014ReturnRe" +
|
||||
"fund\022\021\n\tsignature\030\001 \002(\014\"\035\n\017ProvideContra" +
|
||||
"ct\022\n\n\002tx\030\001 \002(\014\"?\n\rUpdatePayment\022\033\n\023clien" +
|
||||
"t_change_value\030\001 \002(\004\022\021\n\tsignature\030\002 \002(\014\"" +
|
||||
"\363\001\n\005Error\0225\n\004code\030\001 \001(\0162 .paymentchannel",
|
||||
"s.Error.ErrorCode:\005OTHER\022\023\n\013explanation\030" +
|
||||
"\002 \001(\t\"\235\001\n\tErrorCode\022\013\n\007TIMEOUT\020\001\022\020\n\014SYNT" +
|
||||
"AX_ERROR\020\002\022\031\n\025NO_ACCEPTABLE_VERSION\020\003\022\023\n" +
|
||||
"\017BAD_TRANSACTION\020\004\022\031\n\025TIME_WINDOW_TOO_LA" +
|
||||
"RGE\020\005\022\033\n\027CHANNEL_VALUE_TOO_LARGE\020\006\022\t\n\005OT" +
|
||||
"HER\020\007B$\n\032org.bitcoin.paymentchannelB\006Pro" +
|
||||
"tos"
|
||||
"2\036.paymentchannels.UpdatePayment\022%\n\005clos" +
|
||||
"e\030\t \001(\0132\026.paymentchannels.Close\022%\n\005error" +
|
||||
"\030\n \001(\0132\026.paymentchannels.Error\"\274\001\n\013Messa" +
|
||||
"geType\022\022\n\016CLIENT_VERSION\020\001\022\022\n\016SERVER_VER" +
|
||||
"SION\020\002\022\014\n\010INITIATE\020\003\022\022\n\016PROVIDE_REFUND\020\004" +
|
||||
"\022\021\n\rRETURN_REFUND\020\005\022\024\n\020PROVIDE_CONTRACT\020" +
|
||||
"\006\022\020\n\014CHANNEL_OPEN\020\007\022\022\n\016UPDATE_PAYMENT\020\010\022" +
|
||||
"\t\n\005CLOSE\020\t\022\t\n\005ERROR\020\n\"X\n\rClientVersion\022\r",
|
||||
"\n\005major\030\001 \002(\005\022\020\n\005minor\030\002 \001(\005:\0010\022&\n\036previ" +
|
||||
"ous_channel_contract_hash\030\003 \001(\014\"0\n\rServe" +
|
||||
"rVersion\022\r\n\005major\030\001 \002(\005\022\020\n\005minor\030\002 \001(\005:\001" +
|
||||
"0\"]\n\010Initiate\022\024\n\014multisig_key\030\001 \002(\014\022!\n\031m" +
|
||||
"in_accepted_channel_size\030\002 \002(\004\022\030\n\020expire" +
|
||||
"_time_secs\030\003 \002(\004\"1\n\rProvideRefund\022\024\n\014mul" +
|
||||
"tisig_key\030\001 \002(\014\022\n\n\002tx\030\002 \002(\014\"!\n\014ReturnRef" +
|
||||
"und\022\021\n\tsignature\030\001 \002(\014\"\035\n\017ProvideContrac" +
|
||||
"t\022\n\n\002tx\030\001 \002(\014\"?\n\rUpdatePayment\022\033\n\023client" +
|
||||
"_change_value\030\001 \002(\004\022\021\n\tsignature\030\002 \002(\014\"\023",
|
||||
"\n\005Close\022\n\n\002tx\030\003 \002(\014\"\363\001\n\005Error\0225\n\004code\030\001 " +
|
||||
"\001(\0162 .paymentchannels.Error.ErrorCode:\005O" +
|
||||
"THER\022\023\n\013explanation\030\002 \001(\t\"\235\001\n\tErrorCode\022" +
|
||||
"\013\n\007TIMEOUT\020\001\022\020\n\014SYNTAX_ERROR\020\002\022\031\n\025NO_ACC" +
|
||||
"EPTABLE_VERSION\020\003\022\023\n\017BAD_TRANSACTION\020\004\022\031" +
|
||||
"\n\025TIME_WINDOW_TOO_LARGE\020\005\022\033\n\027CHANNEL_VAL" +
|
||||
"UE_TOO_LARGE\020\006\022\t\n\005OTHER\020\007B$\n\032org.bitcoin" +
|
||||
".paymentchannelB\006Protos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@ -7347,7 +8016,7 @@ public final class Protos {
|
||||
internal_static_paymentchannels_TwoWayChannelMessage_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_paymentchannels_TwoWayChannelMessage_descriptor,
|
||||
new java.lang.String[] { "Type", "ClientVersion", "ServerVersion", "Initiate", "ProvideRefund", "ReturnRefund", "ProvideContract", "UpdatePayment", "Error", });
|
||||
new java.lang.String[] { "Type", "ClientVersion", "ServerVersion", "Initiate", "ProvideRefund", "ReturnRefund", "ProvideContract", "UpdatePayment", "Close", "Error", });
|
||||
internal_static_paymentchannels_ClientVersion_descriptor =
|
||||
getDescriptor().getMessageTypes().get(1);
|
||||
internal_static_paymentchannels_ClientVersion_fieldAccessorTable = new
|
||||
@ -7390,8 +8059,14 @@ public final class Protos {
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_paymentchannels_UpdatePayment_descriptor,
|
||||
new java.lang.String[] { "ClientChangeValue", "Signature", });
|
||||
internal_static_paymentchannels_Error_descriptor =
|
||||
internal_static_paymentchannels_Close_descriptor =
|
||||
getDescriptor().getMessageTypes().get(8);
|
||||
internal_static_paymentchannels_Close_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_paymentchannels_Close_descriptor,
|
||||
new java.lang.String[] { "Tx", });
|
||||
internal_static_paymentchannels_Error_descriptor =
|
||||
getDescriptor().getMessageTypes().get(9);
|
||||
internal_static_paymentchannels_Error_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_paymentchannels_Error_descriptor,
|
||||
|
@ -47,15 +47,21 @@ message TwoWayChannelMessage {
|
||||
// message.
|
||||
CHANNEL_OPEN = 7;
|
||||
UPDATE_PAYMENT = 8;
|
||||
// Note that there are no optional fields set for CLOSE, it is sent by either party to
|
||||
// indicate that the channel is now closed and no further updates can happen. After this,
|
||||
// the secondary takes the most recent signature it received in an UPDATE_PAYMENT and
|
||||
// uses it to create a valid transaction, which it then broadcasts on the network.
|
||||
// Either side can send this message. If the client sends it to the server, then the server
|
||||
// takes the most recent signature it received in an UPDATE_PAYMENT and uses it to create a
|
||||
// valid transaction, which it then broadcasts on the network.
|
||||
//
|
||||
// If the server sends it to the client, then it can either be a simple "good bye" message or
|
||||
// it can include the fully signed close transaction and give it back so the client can
|
||||
// eventually broadcast it as well, in case the server fails to do so for some reason (and
|
||||
// more usefully so the client can build on it). In that case the server replies to a CLOSE
|
||||
// with another CLOSE and then disconnects.
|
||||
CLOSE = 9;
|
||||
|
||||
// Used to indicate an error condition.
|
||||
// Both parties should make an effort to send either an ERROR or a CLOSE immediately
|
||||
// before closing the socket (unless they just received an ERROR or a CLOSE)
|
||||
// before closing the socket (unless they just received an ERROR or a CLOSE). This is important
|
||||
// because the protocol may not run over TCP.
|
||||
ERROR = 10;
|
||||
};
|
||||
|
||||
@ -72,6 +78,7 @@ message TwoWayChannelMessage {
|
||||
optional ReturnRefund return_refund = 6;
|
||||
optional ProvideContract provide_contract = 7;
|
||||
optional UpdatePayment update_payment = 8;
|
||||
optional Close close = 9;
|
||||
|
||||
optional Error error = 10;
|
||||
}
|
||||
@ -185,6 +192,11 @@ message UpdatePayment {
|
||||
required bytes signature = 2;
|
||||
}
|
||||
|
||||
message Close {
|
||||
// A copy of the fully signed final contract that closes the channel. The client can verify
|
||||
// the transaction is correct and then commit it to their wallet.
|
||||
required bytes tx = 3;
|
||||
}
|
||||
|
||||
// An Error can be sent by either party at any time
|
||||
// Both parties should make an effort to send either an ERROR or a CLOSE immediately before
|
||||
|
@ -644,13 +644,23 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
client.incrementPayment(Utils.CENT);
|
||||
client.incrementPayment(Utils.CENT);
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
|
||||
pair.serverRecorder.q.take();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
|
||||
pair.serverRecorder.q.take();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT));
|
||||
pair.serverRecorder.q.take();
|
||||
|
||||
// Close it and verify it's considered to be closed.
|
||||
broadcastTxPause.release();
|
||||
client.close();
|
||||
server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLOSE));
|
||||
Transaction close = broadcasts.take();
|
||||
// Server sends back the close TX it just broadcast.
|
||||
final Protos.TwoWayChannelMessage closeMsg = pair.serverRecorder.checkNextMsg(MessageType.CLOSE);
|
||||
final Transaction closeTx = new Transaction(params, closeMsg.getClose().getTx().toByteArray());
|
||||
assertEquals(close, closeTx);
|
||||
client.receiveMessage(closeMsg);
|
||||
assertNotNull(wallet.getTransaction(closeTx.getHash())); // Close TX entered the wallet.
|
||||
sendMoneyToWallet(close, AbstractBlockChain.NewBlockType.BEST_CHAIN);
|
||||
client.connectionClosed();
|
||||
server.connectionClosed();
|
||||
|
@ -20,7 +20,6 @@ import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
import com.google.bitcoin.script.ScriptBuilder;
|
||||
import com.google.bitcoin.utils.TestWithWallet;
|
||||
import com.google.bitcoin.wallet.DefaultCoinSelector;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
@ -189,29 +188,19 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
final TxFuturePair pair2 = broadcasts.take();
|
||||
Transaction closeTx = pair2.tx;
|
||||
pair2.future.set(closeTx);
|
||||
final Transaction reserializedCloseTx = new Transaction(params, closeTx.bitcoinSerialize());
|
||||
assertEquals(PaymentChannelServerState.State.CLOSED, serverState.getState());
|
||||
// ... and on the client side.
|
||||
wallet.receivePending(reserializedCloseTx, null);
|
||||
assertEquals(PaymentChannelClientState.State.CLOSED, clientState.getState());
|
||||
|
||||
// Create a block with the payment transaction in it and give it to both wallets
|
||||
chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), new Transaction(params, closeTx.bitcoinSerialize())));
|
||||
chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), reserializedCloseTx));
|
||||
|
||||
assertEquals(size.multiply(BigInteger.valueOf(5)), serverWallet.getBalance(new DefaultCoinSelector() {
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
assertEquals(size.multiply(BigInteger.valueOf(5)), serverWallet.getBalance());
|
||||
assertEquals(0, serverWallet.getPendingTransactions().size());
|
||||
|
||||
assertEquals(Utils.COIN.subtract(size.multiply(BigInteger.valueOf(5))), wallet.getBalance(new DefaultCoinSelector() {
|
||||
@Override
|
||||
protected boolean shouldSelect(Transaction tx) {
|
||||
if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
assertEquals(Utils.COIN.subtract(size.multiply(BigInteger.valueOf(5))), wallet.getBalance());
|
||||
assertEquals(0, wallet.getPendingTransactions().size());
|
||||
assertEquals(3, wallet.getTransactions(false).size());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user