mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-11 17:55:53 +00:00
PaymentChannelClient support encrypted wallets. Store will upgrade forward gracefully.
This commit is contained in:
parent
c017070398
commit
df00b1e27e
@ -225,7 +225,8 @@ class ConnectionHandler implements MessageWriteTarget {
|
||||
} catch (Exception e) {
|
||||
// This can happen eg if the channel closes while the thread is about to get killed
|
||||
// (ClosedByInterruptException), or if handler.parser.receiveBytes throws something
|
||||
log.error("Error handling SelectionKey: {}", Throwables.getRootCause(e).getMessage());
|
||||
Throwable t = Throwables.getRootCause(e);
|
||||
log.error("Error handling SelectionKey: {}", t.getMessage() != null ? t.getMessage() : t.getClass().getName());
|
||||
handler.closeConnection();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: storedclientpaymentchannel.proto
|
||||
// source: core/src/storedclientpaymentchannel.proto
|
||||
|
||||
package org.bitcoinj.protocols.channels;
|
||||
|
||||
@ -735,13 +735,31 @@ public final class ClientState {
|
||||
*/
|
||||
com.google.protobuf.ByteString getRefundTransaction();
|
||||
|
||||
// required bytes myPublicKey = 8;
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
boolean hasMyPublicKey();
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
com.google.protobuf.ByteString getMyPublicKey();
|
||||
|
||||
// required bytes myKey = 4;
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
boolean hasMyKey();
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
com.google.protobuf.ByteString getMyKey();
|
||||
|
||||
@ -859,25 +877,30 @@ public final class ClientState {
|
||||
break;
|
||||
}
|
||||
case 34: {
|
||||
bitField0_ |= 0x00000008;
|
||||
bitField0_ |= 0x00000010;
|
||||
myKey_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
bitField0_ |= 0x00000010;
|
||||
bitField0_ |= 0x00000020;
|
||||
valueToMe_ = input.readUInt64();
|
||||
break;
|
||||
}
|
||||
case 48: {
|
||||
bitField0_ |= 0x00000020;
|
||||
bitField0_ |= 0x00000040;
|
||||
refundFees_ = input.readUInt64();
|
||||
break;
|
||||
}
|
||||
case 58: {
|
||||
bitField0_ |= 0x00000040;
|
||||
bitField0_ |= 0x00000080;
|
||||
closeTransactionHash_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
case 66: {
|
||||
bitField0_ |= 0x00000008;
|
||||
myPublicKey_ = input.readBytes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
@ -966,17 +989,41 @@ public final class ClientState {
|
||||
return refundTransaction_;
|
||||
}
|
||||
|
||||
// required bytes myPublicKey = 8;
|
||||
public static final int MYPUBLICKEY_FIELD_NUMBER = 8;
|
||||
private com.google.protobuf.ByteString myPublicKey_;
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public boolean hasMyPublicKey() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getMyPublicKey() {
|
||||
return myPublicKey_;
|
||||
}
|
||||
|
||||
// required bytes myKey = 4;
|
||||
public static final int MYKEY_FIELD_NUMBER = 4;
|
||||
private com.google.protobuf.ByteString myKey_;
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasMyKey() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getMyKey() {
|
||||
return myKey_;
|
||||
@ -989,7 +1036,7 @@ public final class ClientState {
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
*/
|
||||
public boolean hasValueToMe() {
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
}
|
||||
/**
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
@ -1005,7 +1052,7 @@ public final class ClientState {
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
*/
|
||||
public boolean hasRefundFees() {
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
return ((bitField0_ & 0x00000040) == 0x00000040);
|
||||
}
|
||||
/**
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
@ -1027,7 +1074,7 @@ public final class ClientState {
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasCloseTransactionHash() {
|
||||
return ((bitField0_ & 0x00000040) == 0x00000040);
|
||||
return ((bitField0_ & 0x00000080) == 0x00000080);
|
||||
}
|
||||
/**
|
||||
* <code>optional bytes closeTransactionHash = 7;</code>
|
||||
@ -1046,6 +1093,7 @@ public final class ClientState {
|
||||
id_ = com.google.protobuf.ByteString.EMPTY;
|
||||
contractTransaction_ = com.google.protobuf.ByteString.EMPTY;
|
||||
refundTransaction_ = com.google.protobuf.ByteString.EMPTY;
|
||||
myPublicKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
myKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
valueToMe_ = 0L;
|
||||
refundFees_ = 0L;
|
||||
@ -1068,6 +1116,10 @@ public final class ClientState {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
if (!hasMyPublicKey()) {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
}
|
||||
if (!hasMyKey()) {
|
||||
memoizedIsInitialized = 0;
|
||||
return false;
|
||||
@ -1096,18 +1148,21 @@ public final class ClientState {
|
||||
if (((bitField0_ & 0x00000004) == 0x00000004)) {
|
||||
output.writeBytes(3, refundTransaction_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
output.writeBytes(4, myKey_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
output.writeUInt64(5, valueToMe_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
if (((bitField0_ & 0x00000040) == 0x00000040)) {
|
||||
output.writeUInt64(6, refundFees_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000040) == 0x00000040)) {
|
||||
if (((bitField0_ & 0x00000080) == 0x00000080)) {
|
||||
output.writeBytes(7, closeTransactionHash_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
output.writeBytes(8, myPublicKey_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@ -1129,22 +1184,26 @@ public final class ClientState {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(3, refundTransaction_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(4, myKey_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt64Size(5, valueToMe_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
if (((bitField0_ & 0x00000040) == 0x00000040)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt64Size(6, refundFees_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000040) == 0x00000040)) {
|
||||
if (((bitField0_ & 0x00000080) == 0x00000080)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(7, closeTransactionHash_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBytesSize(8, myPublicKey_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
@ -1272,14 +1331,16 @@ public final class ClientState {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
refundTransaction_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000004);
|
||||
myKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
myPublicKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
valueToMe_ = 0L;
|
||||
myKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
refundFees_ = 0L;
|
||||
valueToMe_ = 0L;
|
||||
bitField0_ = (bitField0_ & ~0x00000020);
|
||||
closeTransactionHash_ = com.google.protobuf.ByteString.EMPTY;
|
||||
refundFees_ = 0L;
|
||||
bitField0_ = (bitField0_ & ~0x00000040);
|
||||
closeTransactionHash_ = com.google.protobuf.ByteString.EMPTY;
|
||||
bitField0_ = (bitField0_ & ~0x00000080);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1323,18 +1384,22 @@ public final class ClientState {
|
||||
if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
|
||||
to_bitField0_ |= 0x00000008;
|
||||
}
|
||||
result.myKey_ = myKey_;
|
||||
result.myPublicKey_ = myPublicKey_;
|
||||
if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
|
||||
to_bitField0_ |= 0x00000010;
|
||||
}
|
||||
result.valueToMe_ = valueToMe_;
|
||||
result.myKey_ = myKey_;
|
||||
if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
|
||||
to_bitField0_ |= 0x00000020;
|
||||
}
|
||||
result.refundFees_ = refundFees_;
|
||||
result.valueToMe_ = valueToMe_;
|
||||
if (((from_bitField0_ & 0x00000040) == 0x00000040)) {
|
||||
to_bitField0_ |= 0x00000040;
|
||||
}
|
||||
result.refundFees_ = refundFees_;
|
||||
if (((from_bitField0_ & 0x00000080) == 0x00000080)) {
|
||||
to_bitField0_ |= 0x00000080;
|
||||
}
|
||||
result.closeTransactionHash_ = closeTransactionHash_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
@ -1361,6 +1426,9 @@ public final class ClientState {
|
||||
if (other.hasRefundTransaction()) {
|
||||
setRefundTransaction(other.getRefundTransaction());
|
||||
}
|
||||
if (other.hasMyPublicKey()) {
|
||||
setMyPublicKey(other.getMyPublicKey());
|
||||
}
|
||||
if (other.hasMyKey()) {
|
||||
setMyKey(other.getMyKey());
|
||||
}
|
||||
@ -1390,6 +1458,10 @@ public final class ClientState {
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!hasMyPublicKey()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!hasMyKey()) {
|
||||
|
||||
return false;
|
||||
@ -1532,37 +1604,89 @@ public final class ClientState {
|
||||
return this;
|
||||
}
|
||||
|
||||
// required bytes myPublicKey = 8;
|
||||
private com.google.protobuf.ByteString myPublicKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public boolean hasMyPublicKey() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getMyPublicKey() {
|
||||
return myPublicKey_;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public Builder setMyPublicKey(com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000008;
|
||||
myPublicKey_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myPublicKey = 8;</code>
|
||||
*/
|
||||
public Builder clearMyPublicKey() {
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
myPublicKey_ = getDefaultInstance().getMyPublicKey();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// required bytes myKey = 4;
|
||||
private com.google.protobuf.ByteString myKey_ = com.google.protobuf.ByteString.EMPTY;
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasMyKey() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public com.google.protobuf.ByteString getMyKey() {
|
||||
return myKey_;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public Builder setMyKey(com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000008;
|
||||
bitField0_ |= 0x00000010;
|
||||
myKey_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>required bytes myKey = 4;</code>
|
||||
*
|
||||
* <pre>
|
||||
* Deprecated, key is stored in the wallet, and found using myPublicKey;
|
||||
* </pre>
|
||||
*/
|
||||
public Builder clearMyKey() {
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
myKey_ = getDefaultInstance().getMyKey();
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1574,7 +1698,7 @@ public final class ClientState {
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
*/
|
||||
public boolean hasValueToMe() {
|
||||
return ((bitField0_ & 0x00000010) == 0x00000010);
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
}
|
||||
/**
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
@ -1586,7 +1710,7 @@ public final class ClientState {
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
*/
|
||||
public Builder setValueToMe(long value) {
|
||||
bitField0_ |= 0x00000010;
|
||||
bitField0_ |= 0x00000020;
|
||||
valueToMe_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1595,7 +1719,7 @@ public final class ClientState {
|
||||
* <code>required uint64 valueToMe = 5;</code>
|
||||
*/
|
||||
public Builder clearValueToMe() {
|
||||
bitField0_ = (bitField0_ & ~0x00000010);
|
||||
bitField0_ = (bitField0_ & ~0x00000020);
|
||||
valueToMe_ = 0L;
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1607,7 +1731,7 @@ public final class ClientState {
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
*/
|
||||
public boolean hasRefundFees() {
|
||||
return ((bitField0_ & 0x00000020) == 0x00000020);
|
||||
return ((bitField0_ & 0x00000040) == 0x00000040);
|
||||
}
|
||||
/**
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
@ -1619,7 +1743,7 @@ public final class ClientState {
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
*/
|
||||
public Builder setRefundFees(long value) {
|
||||
bitField0_ |= 0x00000020;
|
||||
bitField0_ |= 0x00000040;
|
||||
refundFees_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1628,7 +1752,7 @@ public final class ClientState {
|
||||
* <code>required uint64 refundFees = 6;</code>
|
||||
*/
|
||||
public Builder clearRefundFees() {
|
||||
bitField0_ = (bitField0_ & ~0x00000020);
|
||||
bitField0_ = (bitField0_ & ~0x00000040);
|
||||
refundFees_ = 0L;
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1646,7 +1770,7 @@ public final class ClientState {
|
||||
* </pre>
|
||||
*/
|
||||
public boolean hasCloseTransactionHash() {
|
||||
return ((bitField0_ & 0x00000040) == 0x00000040);
|
||||
return ((bitField0_ & 0x00000080) == 0x00000080);
|
||||
}
|
||||
/**
|
||||
* <code>optional bytes closeTransactionHash = 7;</code>
|
||||
@ -1673,7 +1797,7 @@ public final class ClientState {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000040;
|
||||
bitField0_ |= 0x00000080;
|
||||
closeTransactionHash_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1688,7 +1812,7 @@ public final class ClientState {
|
||||
* </pre>
|
||||
*/
|
||||
public Builder clearCloseTransactionHash() {
|
||||
bitField0_ = (bitField0_ & ~0x00000040);
|
||||
bitField0_ = (bitField0_ & ~0x00000080);
|
||||
closeTransactionHash_ = getDefaultInstance().getCloseTransactionHash();
|
||||
onChanged();
|
||||
return this;
|
||||
@ -1724,16 +1848,17 @@ public final class ClientState {
|
||||
descriptor;
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n storedclientpaymentchannel.proto\022\017paym" +
|
||||
"entchannels\"\\\n\033StoredClientPaymentChanne" +
|
||||
"ls\022=\n\010channels\030\001 \003(\0132+.paymentchannels.S" +
|
||||
"toredClientPaymentChannel\"\264\001\n\032StoredClie" +
|
||||
"ntPaymentChannel\022\n\n\002id\030\001 \002(\014\022\033\n\023contract" +
|
||||
"Transaction\030\002 \002(\014\022\031\n\021refundTransaction\030\003" +
|
||||
" \002(\014\022\r\n\005myKey\030\004 \002(\014\022\021\n\tvalueToMe\030\005 \002(\004\022\022" +
|
||||
"\n\nrefundFees\030\006 \002(\004\022\034\n\024closeTransactionHa" +
|
||||
"sh\030\007 \001(\014B.\n\037org.bitcoinj.protocols.chann" +
|
||||
"elsB\013ClientState"
|
||||
"\n)core/src/storedclientpaymentchannel.pr" +
|
||||
"oto\022\017paymentchannels\"\\\n\033StoredClientPaym" +
|
||||
"entChannels\022=\n\010channels\030\001 \003(\0132+.paymentc" +
|
||||
"hannels.StoredClientPaymentChannel\"\311\001\n\032S" +
|
||||
"toredClientPaymentChannel\022\n\n\002id\030\001 \002(\014\022\033\n" +
|
||||
"\023contractTransaction\030\002 \002(\014\022\031\n\021refundTran" +
|
||||
"saction\030\003 \002(\014\022\023\n\013myPublicKey\030\010 \002(\014\022\r\n\005my" +
|
||||
"Key\030\004 \002(\014\022\021\n\tvalueToMe\030\005 \002(\004\022\022\n\nrefundFe" +
|
||||
"es\030\006 \002(\004\022\034\n\024closeTransactionHash\030\007 \001(\014B." +
|
||||
"\n\037org.bitcoinj.protocols.channelsB\013Clien",
|
||||
"tState"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@ -1751,7 +1876,7 @@ public final class ClientState {
|
||||
internal_static_paymentchannels_StoredClientPaymentChannel_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_paymentchannels_StoredClientPaymentChannel_descriptor,
|
||||
new java.lang.String[] { "Id", "ContractTransaction", "RefundTransaction", "MyKey", "ValueToMe", "RefundFees", "CloseTransactionHash", });
|
||||
new java.lang.String[] { "Id", "ContractTransaction", "RefundTransaction", "MyPublicKey", "MyKey", "ValueToMe", "RefundFees", "CloseTransactionHash", });
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
@ -17,11 +17,13 @@
|
||||
package org.bitcoinj.protocols.channels;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.bitcoin.paymentchannel.Protos;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -82,9 +84,12 @@ public interface IPaymentChannelClient {
|
||||
* ({@link PaymentChannelClientConnection#state()}.getTotalValue())
|
||||
* @throws IllegalStateException If the channel has been closed or is not yet open
|
||||
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
|
||||
* @throws ECKey.KeyIsEncryptedException If the keys are encrypted and no AES key has been provided,
|
||||
* @return a future that completes when the server acknowledges receipt and acceptance of the payment.
|
||||
*/
|
||||
ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException;
|
||||
ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info,
|
||||
@Nullable KeyParameter userKey)
|
||||
throws ValueOutOfRangeException, IllegalStateException, ECKey.KeyIsEncryptedException;
|
||||
|
||||
/**
|
||||
* Implements the connection between this client and the server, providing an interface which allows messages to be
|
||||
|
@ -28,9 +28,9 @@ import com.google.protobuf.ByteString;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import org.bitcoin.paymentchannel.Protos;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -93,6 +93,9 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
|
||||
private Coin missing;
|
||||
|
||||
// key to decrypt myKey, if it is encrypted, during setup.
|
||||
private KeyParameter userKeySetup;
|
||||
|
||||
private final long timeWindow;
|
||||
|
||||
@GuardedBy("lock") private long minPayment;
|
||||
@ -127,7 +130,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* the server)
|
||||
*/
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId, ClientConnection conn) {
|
||||
this(wallet,myKey,maxValue,serverId, DEFAULT_TIME_WINDOW, conn);
|
||||
this(wallet,myKey,maxValue,serverId, DEFAULT_TIME_WINDOW, null, conn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,10 +149,12 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* @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, ClientConnection conn) {
|
||||
public PaymentChannelClient(Wallet wallet, ECKey myKey, Coin maxValue, Sha256Hash serverId, long timeWindow,
|
||||
@Nullable KeyParameter userKeySetup, ClientConnection conn) {
|
||||
this.wallet = checkNotNull(wallet);
|
||||
this.myKey = checkNotNull(myKey);
|
||||
this.maxValue = checkNotNull(maxValue);
|
||||
@ -157,6 +162,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
checkState(timeWindow >= 0);
|
||||
this.timeWindow = timeWindow;
|
||||
this.conn = checkNotNull(conn);
|
||||
this.userKeySetup = userKeySetup;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,9 +177,13 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
|
||||
@Nullable
|
||||
@GuardedBy("lock")
|
||||
private CloseReason receiveInitiate(Protos.Initiate initiate, Coin contractValue, Protos.Error.Builder errorBuilder) throws VerificationException, InsufficientMoneyException {
|
||||
private CloseReason receiveInitiate(Protos.Initiate initiate, Coin contractValue, Protos.Error.Builder errorBuilder)
|
||||
throws VerificationException, InsufficientMoneyException, ECKey.KeyIsEncryptedException {
|
||||
log.info("Got INITIATE message:\n{}", initiate.toString());
|
||||
|
||||
if (wallet.isEncrypted() && this.userKeySetup == null)
|
||||
throw new ECKey.KeyIsEncryptedException();
|
||||
|
||||
final long expireTime = initiate.getExpireTimeSecs();
|
||||
checkState( expireTime >= 0 && initiate.getMinAcceptedChannelSize() >= 0);
|
||||
|
||||
@ -207,7 +217,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
throw new VerificationException("Server gave us a non-canonical public key, protocol error.");
|
||||
state = new PaymentChannelClientState(wallet, myKey, ECKey.fromPublicOnly(pubKeyBytes), contractValue, expireTime);
|
||||
try {
|
||||
state.initiate();
|
||||
state.initiate(userKeySetup);
|
||||
} catch (ValueOutOfRangeException e) {
|
||||
log.error("Value out of range when trying to initiate", e);
|
||||
errorBuilder.setCode(Protos.Error.ErrorCode.CHANNEL_VALUE_TOO_LARGE);
|
||||
@ -228,11 +238,11 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
}
|
||||
|
||||
@GuardedBy("lock")
|
||||
private void receiveRefund(Protos.TwoWayChannelMessage refundMsg) throws VerificationException {
|
||||
private void receiveRefund(Protos.TwoWayChannelMessage refundMsg, @Nullable KeyParameter userKey) throws VerificationException {
|
||||
checkState(step == InitStep.WAITING_FOR_REFUND_RETURN && refundMsg.hasReturnRefund());
|
||||
log.info("Got RETURN_REFUND message, providing signed contract");
|
||||
Protos.ReturnRefund returnedRefund = refundMsg.getReturnRefund();
|
||||
state.provideRefundSignature(returnedRefund.getSignature().toByteArray());
|
||||
state.provideRefundSignature(returnedRefund.getSignature().toByteArray(), userKey);
|
||||
step = InitStep.WAITING_FOR_CHANNEL_OPEN;
|
||||
|
||||
// Before we can send the server the contract (ie send it to the network), we must ensure that our refund
|
||||
@ -244,7 +254,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
try {
|
||||
// Make an initial payment of the dust limit, and put it into the message as well. The size of the
|
||||
// server-requested dust limit was already sanity checked by this point.
|
||||
PaymentChannelClientState.IncrementedPayment payment = state().incrementPaymentBy(Coin.valueOf(minPayment));
|
||||
PaymentChannelClientState.IncrementedPayment payment = state().incrementPaymentBy(Coin.valueOf(minPayment), userKey);
|
||||
Protos.UpdatePayment.Builder initialMsg = contractMsg.getInitialPaymentBuilder();
|
||||
initialMsg.setSignature(ByteString.copyFrom(payment.signature.encodeToBitcoin()));
|
||||
initialMsg.setClientChangeValue(state.getValueRefunded().value);
|
||||
@ -311,7 +321,9 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
log.error("Initiate failed with error: {}", errorBuilder.build().toString());
|
||||
break;
|
||||
case RETURN_REFUND:
|
||||
receiveRefund(msg);
|
||||
receiveRefund(msg, userKeySetup);
|
||||
// Key not used anymore
|
||||
userKeySetup = null;
|
||||
return;
|
||||
case CHANNEL_OPEN:
|
||||
receiveChannelOpen();
|
||||
@ -498,7 +510,7 @@ public class PaymentChannelClient implements IPaymentChannelClient {
|
||||
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
|
||||
*/
|
||||
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
|
||||
return incrementPayment(size, null);
|
||||
return incrementPayment(size, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -510,22 +522,28 @@ 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.
|
||||
* @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* @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)
|
||||
* @throws ECKey.KeyIsEncryptedException If the keys are encrypted and no AES key has been provided,
|
||||
*/
|
||||
@Override
|
||||
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info) throws ValueOutOfRangeException, IllegalStateException {
|
||||
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size, @Nullable ByteString info, @Nullable KeyParameter userKey)
|
||||
throws ValueOutOfRangeException, IllegalStateException, ECKey.KeyIsEncryptedException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (state() == null || !connectionOpen || step != InitStep.CHANNEL_OPEN)
|
||||
throw new IllegalStateException("Channel is not fully initialized/has already been closed");
|
||||
if (increasePaymentFuture != null)
|
||||
throw new IllegalStateException("Already incrementing paying, wait for previous payment to complete.");
|
||||
if (wallet.isEncrypted() && userKey == null)
|
||||
throw new ECKey.KeyIsEncryptedException();
|
||||
|
||||
PaymentChannelClientState.IncrementedPayment payment = state().incrementPaymentBy(size);
|
||||
PaymentChannelClientState.IncrementedPayment payment = state().incrementPaymentBy(size, userKey);
|
||||
Protos.UpdatePayment.Builder updatePaymentBuilder = Protos.UpdatePayment.newBuilder()
|
||||
.setSignature(ByteString.copyFrom(payment.signature.encodeToBitcoin()))
|
||||
.setClientChangeValue(state.getValueRefunded().value);
|
||||
|
@ -29,7 +29,9 @@ import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.bitcoin.paymentchannel.Protos;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
@ -45,14 +47,15 @@ public class PaymentChannelClientConnection {
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* 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 already have a {@link StoredPaymentChannelClientStates} object in its extensions set.
|
||||
* 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.
|
||||
@ -65,7 +68,8 @@ 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.DEFAULT_TIME_WINDOW);
|
||||
this(server, timeoutSeconds, wallet, myKey, maxValue, serverId,
|
||||
PaymentChannelClient.DEFAULT_TIME_WINDOW, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +81,8 @@ public class PaymentChannelClientConnection {
|
||||
* @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 already have a {@link StoredPaymentChannelClientStates} object in its extensions set.
|
||||
* Can be encrypted if user key is supplied when needed. 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.
|
||||
@ -85,16 +90,19 @@ public class PaymentChannelClientConnection {
|
||||
* 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) throws IOException, ValueOutOfRangeException {
|
||||
Coin maxValue, String serverId, final long timeWindow,
|
||||
@Nullable KeyParameter userKeySetup)
|
||||
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.create(serverId.getBytes()), timeWindow,
|
||||
new PaymentChannelClient.ClientConnection() {
|
||||
userKeySetup, new PaymentChannelClient.ClientConnection() {
|
||||
@Override
|
||||
public void sendToServer(Protos.TwoWayChannelMessage msg) {
|
||||
wireParser.write(msg);
|
||||
@ -170,7 +178,7 @@ public class PaymentChannelClientConnection {
|
||||
* (see {@link PaymentChannelClientConnection#getChannelOpenFuture()} for the second)
|
||||
*/
|
||||
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size) throws ValueOutOfRangeException, IllegalStateException {
|
||||
return channelClient.incrementPayment(size, null);
|
||||
return channelClient.incrementPayment(size, null, null);
|
||||
}
|
||||
/**
|
||||
* Increments the total value which we pay the server.
|
||||
@ -182,8 +190,28 @@ 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<PaymentIncrementAck> incrementPayment(Coin size, ByteString info) throws ValueOutOfRangeException, IllegalStateException {
|
||||
return channelClient.incrementPayment(size, info);
|
||||
return channelClient.incrementPayment(size, info, null);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Increments the total value which we pay the server.
|
||||
*
|
||||
* @param size How many satoshis to increment the payment by (note: not the new total).
|
||||
* @param info Information about this payment increment, used to extend this protocol.
|
||||
* @param userKey Key derived from a user password, needed for any signing when the wallet is encrypted.
|
||||
* The wallet KeyCrypter is assumed.
|
||||
* @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)
|
||||
*/
|
||||
public ListenableFuture<PaymentIncrementAck> incrementPayment(Coin size,
|
||||
@Nullable ByteString info,
|
||||
@Nullable KeyParameter userKey)
|
||||
throws ValueOutOfRangeException, IllegalStateException {
|
||||
return channelClient.incrementPayment(size, info, userKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,6 +22,7 @@ import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptBuilder;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.AllowUnconfirmedCoinSelector;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
@ -31,6 +32,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
@ -59,7 +61,7 @@ import static com.google.common.base.Preconditions.*;
|
||||
* 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 PaymentChannelClientState#getIncompleteRefundTransaction()} and pass the resultant transaction through to the
|
||||
* server. Once you have retrieved the signature, use {@link PaymentChannelClientState#provideRefundSignature(byte[])}.
|
||||
* server. Once you have retrieved the signature, use {@link PaymentChannelClientState#provideRefundSignature(byte[], KeyParameter)}.
|
||||
* 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 PaymentChannelClientState#getMultisigContract()}) safely.
|
||||
@ -149,7 +151,7 @@ public class PaymentChannelClientState {
|
||||
* @param myKey a freshly generated private key for this channel.
|
||||
* @param serverMultisigKey a public key retrieved from the server used for the initial multisig contract
|
||||
* @param value how many satoshis to put into this contract. If the channel reaches this limit, it must be closed.
|
||||
* It is suggested you use at least {@link Utils#CENT} to avoid paying fees if you need to spend the refund transaction
|
||||
* It is suggested you use at least {@link Coin#CENT} to avoid paying fees if you need to spend the refund transaction
|
||||
* @param expiryTimeInSeconds At what point (UNIX timestamp +/- a few hours) the channel will expire
|
||||
*
|
||||
* @throws VerificationException If either myKey's pubkey or serverMultisigKey's pubkey are non-canonical (ie invalid)
|
||||
@ -235,7 +237,23 @@ public class PaymentChannelClientState {
|
||||
* @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 synchronized void initiate() throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
public void initiate() throws ValueOutOfRangeException, InsufficientMoneyException {
|
||||
initiate(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the initial multisig contract and incomplete refund transaction which can be requested at the appropriate
|
||||
* time using {@link PaymentChannelClientState#getIncompleteRefundTransaction} and
|
||||
* {@link PaymentChannelClientState#getMultisigContract()}. The way the contract is crafted can be adjusted by
|
||||
* overriding {@link PaymentChannelClientState#editContractSendRequest(org.bitcoinj.core.Wallet.SendRequest)}.
|
||||
* 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.
|
||||
*
|
||||
* @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 synchronized void initiate(@Nullable KeyParameter userKey) 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
|
||||
@ -251,6 +269,7 @@ public class PaymentChannelClientState {
|
||||
req.coinSelector = AllowUnconfirmedCoinSelector.get();
|
||||
editContractSendRequest(req);
|
||||
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
|
||||
req.aesKey = userKey;
|
||||
wallet.completeTx(req);
|
||||
Coin multisigFee = req.tx.getFee();
|
||||
multisigContract = req.tx;
|
||||
@ -291,7 +310,7 @@ public class PaymentChannelClientState {
|
||||
|
||||
/**
|
||||
* Returns the transaction that locks the money to the agreement of both parties. Do not mutate the result.
|
||||
* Once this step is done, you can use {@link PaymentChannelClientState#incrementPaymentBy(Coin)} to
|
||||
* Once this step is done, you can use {@link PaymentChannelClientState#incrementPaymentBy(Coin, KeyParameter)} to
|
||||
* start paying the server.
|
||||
*/
|
||||
public synchronized Transaction getMultisigContract() {
|
||||
@ -304,7 +323,7 @@ public class PaymentChannelClientState {
|
||||
/**
|
||||
* Returns a partially signed (invalid) refund transaction that should be passed to the server. Once the server
|
||||
* has checked it out and provided its own signature, call
|
||||
* {@link PaymentChannelClientState#provideRefundSignature(byte[])} with the result.
|
||||
* {@link PaymentChannelClientState#provideRefundSignature(byte[], KeyParameter)} with the result.
|
||||
*/
|
||||
public synchronized Transaction getIncompleteRefundTransaction() {
|
||||
checkState(refundTx != null);
|
||||
@ -322,7 +341,8 @@ public class PaymentChannelClientState {
|
||||
* transaction are automatically committed to wallet so that it can handle broadcasting the refund transaction at
|
||||
* the appropriate time if necessary.</p>
|
||||
*/
|
||||
public synchronized void provideRefundSignature(byte[] theirSignature) throws VerificationException {
|
||||
public synchronized void provideRefundSignature(byte[] theirSignature, @Nullable KeyParameter userKey)
|
||||
throws VerificationException {
|
||||
checkNotNull(theirSignature);
|
||||
checkState(state == State.WAITING_FOR_SIGNED_REFUND);
|
||||
TransactionSignature theirSig = TransactionSignature.decodeFromBitcoin(theirSignature, true);
|
||||
@ -336,7 +356,8 @@ public class PaymentChannelClientState {
|
||||
throw new RuntimeException(e); // Cannot happen: we built this ourselves.
|
||||
}
|
||||
TransactionSignature ourSignature =
|
||||
refundTx.calculateSignature(0, myKey, multisigScript, Transaction.SigHash.ALL, false);
|
||||
refundTx.calculateSignature(0, myKey.maybeDecrypt(userKey),
|
||||
multisigScript, Transaction.SigHash.ALL, false);
|
||||
// Insert the signatures.
|
||||
Script scriptSig = ScriptBuilder.createMultiSigInputScript(ourSignature, theirSig);
|
||||
log.info("Refund scriptSig: {}", scriptSig);
|
||||
@ -390,7 +411,8 @@ public class PaymentChannelClientState {
|
||||
* @throws ValueOutOfRangeException If size is negative or the channel does not have sufficient money in it to
|
||||
* complete this payment.
|
||||
*/
|
||||
public synchronized IncrementedPayment incrementPaymentBy(Coin size) throws ValueOutOfRangeException {
|
||||
public synchronized IncrementedPayment incrementPaymentBy(Coin size, @Nullable KeyParameter userKey)
|
||||
throws ValueOutOfRangeException {
|
||||
checkState(state == State.READY);
|
||||
checkNotExpired();
|
||||
checkNotNull(size); // Validity of size will be checked by makeUnsignedChannelContract.
|
||||
@ -413,7 +435,7 @@ public class PaymentChannelClientState {
|
||||
mode = Transaction.SigHash.NONE;
|
||||
else
|
||||
mode = Transaction.SigHash.SINGLE;
|
||||
TransactionSignature sig = tx.calculateSignature(0, myKey, multisigScript, mode, true);
|
||||
TransactionSignature sig = tx.calculateSignature(0, myKey.maybeDecrypt(userKey), multisigScript, mode, true);
|
||||
valueToMe = newValueToMe;
|
||||
updateChannelInWallet();
|
||||
IncrementedPayment payment = new IncrementedPayment();
|
||||
@ -506,7 +528,7 @@ public class PaymentChannelClientState {
|
||||
|
||||
/**
|
||||
* Once the servers signature over the refund transaction has been received and provided using
|
||||
* {@link PaymentChannelClientState#provideRefundSignature(byte[])} then this
|
||||
* {@link PaymentChannelClientState#provideRefundSignature(byte[], KeyParameter)} then this
|
||||
* method can be called to receive the now valid and broadcastable refund transaction.
|
||||
*/
|
||||
public synchronized Transaction getCompletedRefundTransaction() {
|
||||
|
@ -264,13 +264,14 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
||||
// First a few asserts to make sure things won't break
|
||||
checkState(channel.valueToMe.signum() >= 0 && channel.valueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0);
|
||||
checkState(channel.refundFees.signum() >= 0 && channel.refundFees.compareTo(NetworkParameters.MAX_MONEY) < 0);
|
||||
checkNotNull(channel.myKey.getPrivKeyBytes());
|
||||
checkNotNull(channel.myKey.getPubKey());
|
||||
checkState(channel.refund.getConfidence().getSource() == TransactionConfidence.Source.SELF);
|
||||
final ClientState.StoredClientPaymentChannel.Builder value = ClientState.StoredClientPaymentChannel.newBuilder()
|
||||
.setId(ByteString.copyFrom(channel.id.getBytes()))
|
||||
.setContractTransaction(ByteString.copyFrom(channel.contract.bitcoinSerialize()))
|
||||
.setRefundTransaction(ByteString.copyFrom(channel.refund.bitcoinSerialize()))
|
||||
.setMyKey(ByteString.copyFrom(channel.myKey.getPrivKeyBytes()))
|
||||
.setMyKey(ByteString.copyFrom(new byte[0])) // Not used, but protobuf message requires
|
||||
.setMyPublicKey(ByteString.copyFrom(channel.myKey.getPubKey()))
|
||||
.setValueToMe(channel.valueToMe.value)
|
||||
.setRefundFees(channel.refundFees.value);
|
||||
if (channel.close != null)
|
||||
@ -294,10 +295,13 @@ public class StoredPaymentChannelClientStates implements WalletExtension {
|
||||
for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) {
|
||||
Transaction refundTransaction = new Transaction(params, storedState.getRefundTransaction().toByteArray());
|
||||
refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
|
||||
ECKey myKey = (storedState.getMyKey().isEmpty()) ?
|
||||
containingWallet.findKeyFromPubKey(storedState.getMyPublicKey().toByteArray()) :
|
||||
ECKey.fromPrivate(storedState.getMyKey().toByteArray());
|
||||
StoredClientChannel channel = new StoredClientChannel(new Sha256Hash(storedState.getId().toByteArray()),
|
||||
new Transaction(params, storedState.getContractTransaction().toByteArray()),
|
||||
refundTransaction,
|
||||
ECKey.fromPrivate(storedState.getMyKey().toByteArray()),
|
||||
myKey,
|
||||
Coin.valueOf(storedState.getValueToMe()),
|
||||
Coin.valueOf(storedState.getRefundFees()), false);
|
||||
if (storedState.hasCloseTransactionHash()) {
|
||||
|
@ -39,6 +39,8 @@ message StoredClientPaymentChannel {
|
||||
required bytes id = 1;
|
||||
required bytes contractTransaction = 2;
|
||||
required bytes refundTransaction = 3;
|
||||
required bytes myPublicKey = 8;
|
||||
// Deprecated, key is already stored in the wallet, and found using myPublicKey;
|
||||
required bytes myKey = 4;
|
||||
required uint64 valueToMe = 5;
|
||||
required uint64 refundFees = 6;
|
||||
|
@ -29,6 +29,7 @@ import org.bitcoin.paymentchannel.Protos;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -116,6 +117,20 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
|
||||
@Test
|
||||
public void testSimpleChannel() throws Exception {
|
||||
exectuteSimpleChannelTest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptedClientWallet() throws Exception {
|
||||
// Encrypt the client wallet
|
||||
String mySecretPw = "MySecret";
|
||||
wallet.encrypt(mySecretPw);
|
||||
|
||||
KeyParameter userKeySetup = wallet.getKeyCrypter().deriveKey(mySecretPw);
|
||||
exectuteSimpleChannelTest(userKeySetup);
|
||||
}
|
||||
|
||||
public void exectuteSimpleChannelTest(KeyParameter userKeySetup) throws Exception {
|
||||
// Test with network code and without any issues. We'll broadcast two txns: multisig contract and settle transaction.
|
||||
final SettableFuture<ListenableFuture<PaymentChannelServerState>> serverCloseFuture = SettableFuture.create();
|
||||
final SettableFuture<Sha256Hash> channelOpenFuture = SettableFuture.create();
|
||||
@ -147,7 +162,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
server.bindAndStart(4243);
|
||||
|
||||
PaymentChannelClientConnection client = new PaymentChannelClientConnection(
|
||||
new InetSocketAddress("localhost", 4243), 30, wallet, myKey, COIN, "");
|
||||
new InetSocketAddress("localhost", 4243), 30, wallet, myKey, COIN, "", PaymentChannelClient.DEFAULT_TIME_WINDOW, userKeySetup);
|
||||
|
||||
// Wait for the multi-sig tx to be transmitted.
|
||||
broadcastTxPause.release();
|
||||
@ -177,7 +192,7 @@ public class ChannelConnectionTest extends TestWithWallet {
|
||||
q.take().assertPair(amount, null);
|
||||
for (String info : new String[] {null, "one", "two"} ) {
|
||||
final ByteString bytes = (info==null) ? null :ByteString.copyFromUtf8(info);
|
||||
final PaymentIncrementAck ack = client.incrementPayment(CENT, bytes).get();
|
||||
final PaymentIncrementAck ack = client.incrementPayment(CENT, bytes, userKeySetup).get();
|
||||
if (info != null) {
|
||||
final ByteString ackInfo = ack.getInfo();
|
||||
assertNotNull("Ack info is null", ackInfo);
|
||||
|
@ -6,6 +6,7 @@ import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -49,7 +50,9 @@ public class PaymentChannelClientTest {
|
||||
@Test
|
||||
public void shouldSendTimeWindowInClientVersion() throws Exception {
|
||||
long timeWindow = 4000;
|
||||
PaymentChannelClient dut = new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, timeWindow, connection);
|
||||
KeyParameter userKey = null;
|
||||
PaymentChannelClient dut =
|
||||
new PaymentChannelClient(wallet, ecKey, maxValue, serverHash, timeWindow, userKey, connection);
|
||||
connection.sendToServer(capture(clientVersionCapture));
|
||||
EasyMock.expect(wallet.getExtensions()).andReturn(new HashMap<String, WalletExtension>());
|
||||
replay(connection, wallet);
|
||||
|
@ -130,7 +130,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey());
|
||||
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||
// This verifies that the refund can spend the multi-sig output when run.
|
||||
clientState.provideRefundSignature(refundSig);
|
||||
clientState.provideRefundSignature(refundSig, null);
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
@ -169,7 +169,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
Coin size = halfCoin.divide(100);
|
||||
Coin totalPayment = Coin.ZERO;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
byte[] signature = clientState.incrementPaymentBy(size).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(size);
|
||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signature);
|
||||
}
|
||||
@ -177,7 +177,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
// Now confirm the contract transaction and make sure payments still work
|
||||
chain.add(makeSolvedTestBlock(blockStore.getChainHead().getHeader(), multisigContract));
|
||||
|
||||
byte[] signature = clientState.incrementPaymentBy(size).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(size);
|
||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signature);
|
||||
|
||||
@ -248,7 +248,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey());
|
||||
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||
// This verifies that the refund can spend the multi-sig output when run.
|
||||
clientState.provideRefundSignature(refundSig);
|
||||
clientState.provideRefundSignature(refundSig, null);
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
@ -272,7 +272,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
// Pay a tiny bit
|
||||
serverState.incrementPayment(CENT.divide(2).subtract(CENT.divide(10)),
|
||||
clientState.incrementPaymentBy(CENT.divide(10)).signature.encodeToBitcoin());
|
||||
clientState.incrementPaymentBy(CENT.divide(10), null).signature.encodeToBitcoin());
|
||||
|
||||
// Advance time until our we get close enough to lock time that server should rebroadcast
|
||||
Utils.rollMockClock(60*60*22);
|
||||
@ -319,7 +319,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
try {
|
||||
// After its expired, we cant still increment payment
|
||||
clientState.incrementPaymentBy(CENT);
|
||||
clientState.incrementPaymentBy(CENT, null);
|
||||
fail();
|
||||
} catch (IllegalStateException e) { }
|
||||
}
|
||||
@ -378,7 +378,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
|
||||
refundSigCopy[refundSigCopy.length-1] = (byte) (Transaction.SigHash.NONE.ordinal() + 1);
|
||||
try {
|
||||
clientState.provideRefundSignature(refundSigCopy);
|
||||
clientState.provideRefundSignature(refundSigCopy, null);
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
assertTrue(e.getMessage().contains("SIGHASH_NONE"));
|
||||
@ -387,7 +387,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
|
||||
refundSigCopy[3] ^= 0x42; // Make the signature fail standard checks
|
||||
try {
|
||||
clientState.provideRefundSignature(refundSigCopy);
|
||||
clientState.provideRefundSignature(refundSigCopy, null);
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
assertTrue(e.getMessage().contains("not canonical"));
|
||||
@ -396,7 +396,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
|
||||
refundSigCopy[10] ^= 0x42; // Flip some random bits in the signature (to make it invalid, not just nonstandard)
|
||||
try {
|
||||
clientState.provideRefundSignature(refundSigCopy);
|
||||
clientState.provideRefundSignature(refundSigCopy, null);
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
assertFalse(e.getMessage().contains("not canonical"));
|
||||
@ -404,13 +404,13 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
refundSigCopy = Arrays.copyOf(refundSig, refundSig.length);
|
||||
try { clientState.getCompletedRefundTransaction(); fail(); } catch (IllegalStateException e) {}
|
||||
clientState.provideRefundSignature(refundSigCopy);
|
||||
try { clientState.provideRefundSignature(refundSigCopy); fail(); } catch (IllegalStateException e) {}
|
||||
clientState.provideRefundSignature(refundSigCopy, null);
|
||||
try { clientState.provideRefundSignature(refundSigCopy, null); fail(); } catch (IllegalStateException e) {}
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
|
||||
try { clientState.incrementPaymentBy(Coin.SATOSHI); fail(); } catch (IllegalStateException e) {}
|
||||
try { clientState.incrementPaymentBy(Coin.SATOSHI, null); fail(); } catch (IllegalStateException e) {}
|
||||
|
||||
byte[] multisigContractSerialized = clientState.getMultisigContract().bitcoinSerialize();
|
||||
|
||||
@ -456,11 +456,11 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
Coin size = halfCoin.divide(100);
|
||||
Coin totalPayment = Coin.ZERO;
|
||||
try {
|
||||
clientState.incrementPaymentBy(COIN);
|
||||
clientState.incrementPaymentBy(COIN, null);
|
||||
fail();
|
||||
} catch (ValueOutOfRangeException e) {}
|
||||
|
||||
byte[] signature = clientState.incrementPaymentBy(size).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(size);
|
||||
|
||||
byte[] signatureCopy = Arrays.copyOf(signature, signature.length);
|
||||
@ -491,7 +491,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signature);
|
||||
|
||||
// Pay the rest (signed with SIGHASH_NONE|SIGHASH_ANYONECANPAY)
|
||||
byte[] signature2 = clientState.incrementPaymentBy(halfCoin.subtract(totalPayment)).signature.encodeToBitcoin();
|
||||
byte[] signature2 = clientState.incrementPaymentBy(halfCoin.subtract(totalPayment), null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(halfCoin.subtract(totalPayment));
|
||||
assertEquals(totalPayment, halfCoin);
|
||||
|
||||
@ -512,12 +512,12 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
assertEquals(serverState.getBestValueToMe(), totalPayment);
|
||||
|
||||
try {
|
||||
clientState.incrementPaymentBy(Coin.SATOSHI.negate());
|
||||
clientState.incrementPaymentBy(Coin.SATOSHI.negate(), null);
|
||||
fail();
|
||||
} catch (ValueOutOfRangeException e) {}
|
||||
|
||||
try {
|
||||
clientState.incrementPaymentBy(halfCoin.subtract(size).add(Coin.SATOSHI));
|
||||
clientState.incrementPaymentBy(halfCoin.subtract(size).add(Coin.SATOSHI), null);
|
||||
fail();
|
||||
} catch (ValueOutOfRangeException e) {}
|
||||
}
|
||||
@ -577,7 +577,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey());
|
||||
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||
// This verifies that the refund can spend the multi-sig output when run.
|
||||
clientState.provideRefundSignature(refundSig);
|
||||
clientState.provideRefundSignature(refundSig, null);
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
@ -597,7 +597,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
Coin totalPayment = Coin.ZERO;
|
||||
|
||||
// We can send as little as we want - its up to the server to get the fees right
|
||||
byte[] signature = clientState.incrementPaymentBy(Coin.SATOSHI).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(Coin.SATOSHI, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(Coin.SATOSHI);
|
||||
serverState.incrementPayment(CENT.subtract(totalPayment), signature);
|
||||
|
||||
@ -610,7 +610,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
// We cannot send just under the total value - our refund would make it unspendable. So the client
|
||||
// will correct it for us to be larger than the requested amount, to make the change output zero.
|
||||
PaymentChannelClientState.IncrementedPayment payment =
|
||||
clientState.incrementPaymentBy(CENT.subtract(Transaction.MIN_NONDUST_OUTPUT));
|
||||
clientState.incrementPaymentBy(CENT.subtract(Transaction.MIN_NONDUST_OUTPUT), null);
|
||||
assertEquals(CENT.subtract(SATOSHI), payment.amount);
|
||||
totalPayment = totalPayment.add(payment.amount);
|
||||
|
||||
@ -657,7 +657,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey());
|
||||
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||
// This verifies that the refund can spend the multi-sig output when run.
|
||||
clientState.provideRefundSignature(refundSig);
|
||||
clientState.provideRefundSignature(refundSig, null);
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
@ -680,7 +680,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
assertEquals(PaymentChannelServerState.State.READY, serverState.getState());
|
||||
|
||||
// Both client and server are now in the ready state, split the channel in half
|
||||
byte[] signature = clientState.incrementPaymentBy(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(Coin.SATOSHI))
|
||||
byte[] signature = clientState.incrementPaymentBy(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(Coin.SATOSHI), null)
|
||||
.signature.encodeToBitcoin();
|
||||
Coin totalRefund = CENT.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(SATOSHI));
|
||||
serverState.incrementPayment(totalRefund, signature);
|
||||
@ -705,7 +705,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
assertTrue(e.getMessage().contains("more in fees"));
|
||||
}
|
||||
|
||||
signature = clientState.incrementPaymentBy(SATOSHI.multiply(2)).signature.encodeToBitcoin();
|
||||
signature = clientState.incrementPaymentBy(SATOSHI.multiply(2), null).signature.encodeToBitcoin();
|
||||
totalRefund = totalRefund.subtract(SATOSHI.multiply(2));
|
||||
serverState.incrementPayment(totalRefund, signature);
|
||||
|
||||
@ -738,7 +738,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
byte[] refundSig = serverState.provideRefundTransaction(refund, myKey.getPubKey());
|
||||
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
|
||||
// This verifies that the refund can spend the multi-sig output when run.
|
||||
clientState.provideRefundSignature(refundSig);
|
||||
clientState.provideRefundSignature(refundSig, null);
|
||||
assertEquals(PaymentChannelClientState.State.SAVE_STATE_IN_WALLET, clientState.getState());
|
||||
clientState.fakeSave();
|
||||
assertEquals(PaymentChannelClientState.State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER, clientState.getState());
|
||||
@ -777,7 +777,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
Coin size = halfCoin.divide(100);
|
||||
Coin totalPayment = Coin.ZERO;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
byte[] signature = clientState.incrementPaymentBy(size).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(size);
|
||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signature);
|
||||
}
|
||||
@ -794,7 +794,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
|
||||
|
||||
// Now if we try to spend again the server will reject it since it saw a double-spend
|
||||
try {
|
||||
byte[] signature = clientState.incrementPaymentBy(size).signature.encodeToBitcoin();
|
||||
byte[] signature = clientState.incrementPaymentBy(size, null).signature.encodeToBitcoin();
|
||||
totalPayment = totalPayment.add(size);
|
||||
serverState.incrementPayment(halfCoin.subtract(totalPayment), signature);
|
||||
fail();
|
||||
|
Loading…
x
Reference in New Issue
Block a user