Commit 57656681 authored by Daniel Connolly's avatar Daniel Connolly

adjust v1 payment channels to BCH

parent 1e7b3db2
......@@ -4993,6 +4993,25 @@ public final class Protos {
* </pre>
*/
com.google.protobuf.ByteString getTx();
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
boolean hasAmount();
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
long getAmount();
}
/**
* Protobuf type {@code paymentchannels.ProvideRefund}
......@@ -5060,6 +5079,11 @@ public final class Protos {
tx_ = input.readBytes();
break;
}
case 24: {
bitField0_ |= 0x00000004;
amount_ = input.readUInt64();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
......@@ -5162,9 +5186,35 @@ public final class Protos {
return tx_;
}
public static final int AMOUNT_FIELD_NUMBER = 3;
private long amount_;
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public boolean hasAmount() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public long getAmount() {
return amount_;
}
private void initFields() {
multisigKey_ = com.google.protobuf.ByteString.EMPTY;
tx_ = com.google.protobuf.ByteString.EMPTY;
amount_ = 0L;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
......@@ -5180,6 +5230,10 @@ public final class Protos {
memoizedIsInitialized = 0;
return false;
}
if (!hasAmount()) {
memoizedIsInitialized = 0;
return false;
}
memoizedIsInitialized = 1;
return true;
}
......@@ -5193,6 +5247,9 @@ public final class Protos {
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeBytes(2, tx_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeUInt64(3, amount_);
}
getUnknownFields().writeTo(output);
}
......@@ -5210,6 +5267,10 @@ public final class Protos {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(2, tx_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
size += com.google.protobuf.CodedOutputStream
.computeUInt64Size(3, amount_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
......@@ -5335,6 +5396,8 @@ public final class Protos {
bitField0_ = (bitField0_ & ~0x00000001);
tx_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000002);
amount_ = 0L;
bitField0_ = (bitField0_ & ~0x00000004);
return this;
}
......@@ -5371,6 +5434,10 @@ public final class Protos {
to_bitField0_ |= 0x00000002;
}
result.tx_ = tx_;
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
to_bitField0_ |= 0x00000004;
}
result.amount_ = amount_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
......@@ -5393,6 +5460,9 @@ public final class Protos {
if (other.hasTx()) {
setTx(other.getTx());
}
if (other.hasAmount()) {
setAmount(other.getAmount());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
......@@ -5406,6 +5476,10 @@ public final class Protos {
return false;
}
if (!hasAmount()) {
return false;
}
return true;
}
......@@ -5562,6 +5636,58 @@ public final class Protos {
return this;
}
private long amount_ ;
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public boolean hasAmount() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public long getAmount() {
return amount_;
}
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public Builder setAmount(long value) {
bitField0_ |= 0x00000004;
amount_ = value;
onChanged();
return this;
}
/**
* <code>required uint64 amount = 3;</code>
*
* <pre>
* the amount of the input in the return transaction, in satoshis
* this is required to generate a signature on the return transaction
* </pre>
*/
public Builder clearAmount() {
bitField0_ = (bitField0_ & ~0x00000004);
amount_ = 0L;
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:paymentchannels.ProvideRefund)
}
......@@ -9622,24 +9748,24 @@ public final class Protos {
"\n\005major\030\001 \002(\005\022\020\n\005minor\030\002 \001(\005:\0010\"r\n\010Initi" +
"ate\022\024\n\014multisig_key\030\001 \002(\014\022!\n\031min_accepte" +
"d_channel_size\030\002 \002(\004\022\030\n\020expire_time_secs" +
"\030\003 \002(\004\022\023\n\013min_payment\030\004 \002(\004\"1\n\rProvideRe" +
"fund\022\024\n\014multisig_key\030\001 \002(\014\022\n\n\002tx\030\002 \002(\014\"!",
"\n\014ReturnRefund\022\021\n\tsignature\030\001 \002(\014\"j\n\017Pro" +
"videContract\022\n\n\002tx\030\001 \002(\014\0227\n\017initial_paym" +
"ent\030\002 \002(\0132\036.paymentchannels.UpdatePaymen" +
"t\022\022\n\nclient_key\030\003 \001(\014\"M\n\rUpdatePayment\022\033" +
"\n\023client_change_value\030\001 \002(\004\022\021\n\tsignature" +
"\030\002 \002(\014\022\014\n\004info\030\003 \001(\014\"\032\n\nPaymentAck\022\014\n\004in" +
"fo\030\001 \001(\014\"\030\n\nSettlement\022\n\n\002tx\030\003 \002(\014\"\251\002\n\005E" +
"rror\0225\n\004code\030\001 \001(\0162 .paymentchannels.Err" +
"or.ErrorCode:\005OTHER\022\023\n\013explanation\030\002 \001(\t" +
"\022\026\n\016expected_value\030\003 \001(\004\"\273\001\n\tErrorCode\022\013",
"\n\007TIMEOUT\020\001\022\020\n\014SYNTAX_ERROR\020\002\022\031\n\025NO_ACCE" +
"PTABLE_VERSION\020\003\022\023\n\017BAD_TRANSACTION\020\004\022\034\n" +
"\030TIME_WINDOW_UNACCEPTABLE\020\005\022\033\n\027CHANNEL_V" +
"ALUE_TOO_LARGE\020\006\022\031\n\025MIN_PAYMENT_TOO_LARG" +
"E\020\007\022\t\n\005OTHER\020\010B$\n\032org.bitcoin.paymentcha" +
"nnelB\006Protos"
"\030\003 \002(\004\022\023\n\013min_payment\030\004 \002(\004\"A\n\rProvideRe" +
"fund\022\024\n\014multisig_key\030\001 \002(\014\022\n\n\002tx\030\002 \002(\014\022\016",
"\n\006amount\030\003 \002(\004\"!\n\014ReturnRefund\022\021\n\tsignat" +
"ure\030\001 \002(\014\"j\n\017ProvideContract\022\n\n\002tx\030\001 \002(\014" +
"\0227\n\017initial_payment\030\002 \002(\0132\036.paymentchann" +
"els.UpdatePayment\022\022\n\nclient_key\030\003 \001(\014\"M\n" +
"\rUpdatePayment\022\033\n\023client_change_value\030\001 " +
"\002(\004\022\021\n\tsignature\030\002 \002(\014\022\014\n\004info\030\003 \001(\014\"\032\n\n" +
"PaymentAck\022\014\n\004info\030\001 \001(\014\"\030\n\nSettlement\022\n" +
"\n\002tx\030\003 \002(\014\"\251\002\n\005Error\0225\n\004code\030\001 \001(\0162 .pay" +
"mentchannels.Error.ErrorCode:\005OTHER\022\023\n\013e" +
"xplanation\030\002 \001(\t\022\026\n\016expected_value\030\003 \001(\004",
"\"\273\001\n\tErrorCode\022\013\n\007TIMEOUT\020\001\022\020\n\014SYNTAX_ER" +
"ROR\020\002\022\031\n\025NO_ACCEPTABLE_VERSION\020\003\022\023\n\017BAD_" +
"TRANSACTION\020\004\022\034\n\030TIME_WINDOW_UNACCEPTABL" +
"E\020\005\022\033\n\027CHANNEL_VALUE_TOO_LARGE\020\006\022\031\n\025MIN_" +
"PAYMENT_TOO_LARGE\020\007\022\t\n\005OTHER\020\010B$\n\032org.bi" +
"tcoin.paymentchannelB\006Protos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
......@@ -9682,7 +9808,7 @@ public final class Protos {
internal_static_paymentchannels_ProvideRefund_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_paymentchannels_ProvideRefund_descriptor,
new java.lang.String[] { "MultisigKey", "Tx", });
new java.lang.String[] { "MultisigKey", "Tx", "Amount", });
internal_static_paymentchannels_ReturnRefund_descriptor =
getDescriptor().getMessageTypes().get(5);
internal_static_paymentchannels_ReturnRefund_fieldAccessorTable = new
......
......@@ -403,15 +403,18 @@ public class TransactionInput extends ChildMessage {
* @throws VerificationException If the outpoint doesn't match the given output.
*/
public void verify(TransactionOutput output) throws VerificationException {
Coin inputValue = Coin.ZERO;
if (output.parent != null) {
if (!getOutpoint().getHash().equals(output.getParentTransaction().getHash()))
throw new VerificationException("This input does not refer to the tx containing the output.");
if (getOutpoint().getIndex() != output.getIndex())
throw new VerificationException("This input refers to a different output on the given tx.");
if (getOutpoint().getConnectedOutput() != null)
inputValue = getOutpoint().getConnectedOutput().getValue();
}
Script pubKey = output.getScriptPubKey();
int myIndex = getParentTransaction().getInputs().indexOf(this);
getScriptSig().correctlySpends(getParentTransaction(), myIndex, pubKey);
getScriptSig().correctlySpends(getParentTransaction(), myIndex, pubKey, inputValue, Script.ALL_VERIFY_FLAGS);
}
/**
......
......@@ -335,7 +335,8 @@ public class PaymentChannelClient implements IPaymentChannelClient {
Protos.ProvideRefund.Builder provideRefundBuilder = Protos.ProvideRefund.newBuilder()
.setMultisigKey(ByteString.copyFrom(myKey.getPubKey()))
.setTx(ByteString.copyFrom(((PaymentChannelV1ClientState)state).getIncompleteRefundTransaction().unsafeBitcoinSerialize()));
.setTx(ByteString.copyFrom(((PaymentChannelV1ClientState)state).getIncompleteRefundTransaction().unsafeBitcoinSerialize()))
.setAmount(((PaymentChannelV1ClientState)state).getTotalValue().value);
conn.sendToServer(Protos.TwoWayChannelMessage.newBuilder()
.setProvideRefund(provideRefundBuilder)
......
......@@ -321,6 +321,7 @@ public class PaymentChannelServer {
// We can cast to V1 state since this state is only used in the V1 protocol
byte[] signature = ((PaymentChannelV1ServerState) state)
.provideRefundTransaction(wallet.getParams().getDefaultSerializer().makeTransaction(providedRefund.getTx().toByteArray()),
Coin.valueOf(providedRefund.getAmount()),
providedRefund.getMultisigKey().toByteArray());
step = InitStep.WAITING_ON_CONTRACT;
......
......@@ -138,6 +138,7 @@ public class PaymentChannelV1ClientState extends PaymentChannelClientState {
if (multisigOutput.isDust())
throw new ValueOutOfRangeException("totalValue too small to use");
SendRequest req = SendRequest.forTx(template);
req.setUseForkId(true);
req.coinSelector = AllowUnconfirmedCoinSelector.get();
editContractSendRequest(req);
req.shuffleOutputs = false; // TODO: Fix things so shuffling is usable.
......
......@@ -118,7 +118,7 @@ public class PaymentChannelV1ServerState extends PaymentChannelServerState {
* @return Our signature that makes the refund transaction valid
* @throws VerificationException If the transaction isnt valid or did not meet the requirements of a refund transaction.
*/
public synchronized byte[] provideRefundTransaction(Transaction refundTx, byte[] clientMultiSigPubKey) throws VerificationException {
public synchronized byte[] provideRefundTransaction(Transaction refundTx, Coin inputValue, byte[] clientMultiSigPubKey) throws VerificationException {
checkNotNull(refundTx);
checkNotNull(clientMultiSigPubKey);
stateMachine.checkState(State.WAITING_FOR_REFUND_TRANSACTION);
......@@ -148,7 +148,7 @@ public class PaymentChannelV1ServerState extends PaymentChannelServerState {
// We are really only signing the fact that the transaction has a proper lock time and don't care about anything
// else, so we sign SIGHASH_NONE and SIGHASH_ANYONECANPAY.
TransactionSignature sig = refundTx.getVersion() >= Transaction.FORKID_VERSION ?
refundTx.calculateWitnessSignature(0, serverKey, multisigPubKey, refundTx.getInput(0).getConnectedOutput().getValue(), Transaction.SigHash.NONE, true):
refundTx.calculateWitnessSignature(0, serverKey, multisigPubKey, inputValue, Transaction.SigHash.NONE, true):
refundTx.calculateSignature(0, serverKey, multisigPubKey, Transaction.SigHash.NONE, true);
log.info("Signed refund transaction.");
this.clientOutput = refundTx.getOutput(0);
......
......@@ -4064,7 +4064,7 @@ public class Wallet extends BaseTaggableObject
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
// we sign missing pieces (to check this would require either assuming any signatures are signing
// standard output types or a way to get processed signatures out of script execution)
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey());
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(), txIn.getConnectedOutput().getValue(), Script.ALL_VERIFY_FLAGS);
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
continue;
} catch (ScriptException e) {
......
......@@ -164,6 +164,10 @@ message ProvideRefund {
// * It must have exactly one output which goes back to the primary. This output's
// scriptPubKey will be reused to create payment transactions.
required bytes tx = 2;
// the amount of the input in the return transaction, in satoshis
// this is required to generate a signature on the return transaction
required uint64 amount = 3; // amount is integer-number-of-satoshis
}
// Sent from secondary to primary after it has done initial verification of the refund
......
......@@ -239,7 +239,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
Transaction refund;
if (useRefunds()) {
refund = new Transaction(PARAMS, clientV1State().getIncompleteRefundTransaction().bitcoinSerialize());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
// This verifies that the refund can spend the multi-sig output when run.
clientV1State().provideRefundSignature(refundSig, null);
......@@ -366,7 +366,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
if (useRefunds()) {
// Send the refund tx from client to server and get back the signature.
Transaction refund = new Transaction(PARAMS, clientV1State().getIncompleteRefundTransaction().bitcoinSerialize());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, CENT.divide(2), myKey.getPubKey());
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
// This verifies that the refund can spend the multi-sig output when run.
clientV1State().provideRefundSignature(refundSig, null);
......@@ -475,7 +475,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
Transaction refund = new Transaction(PARAMS, refundTxBytes);
refund.addOutput(Coin.ZERO, new ECKey().toAddress(PARAMS));
try {
serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
fail();
} catch (VerificationException e) {
}
......@@ -483,7 +483,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
refund = new Transaction(PARAMS, refundTxBytes);
refund.addInput(new TransactionInput(PARAMS, refund, new byte[]{}, new TransactionOutPoint(PARAMS, 42, refund.getHash())));
try {
serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
fail();
} catch (VerificationException e) {
}
......@@ -491,7 +491,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
refund = new Transaction(PARAMS, refundTxBytes);
refund.setLockTime(0);
try {
serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
fail();
} catch (VerificationException e) {
}
......@@ -499,15 +499,15 @@ public class PaymentChannelStateTest extends TestWithWallet {
refund = new Transaction(PARAMS, refundTxBytes);
refund.getInput(0).setSequenceNumber(TransactionInput.NO_SEQUENCE);
try {
serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
fail();
} catch (VerificationException e) {
}
refund = new Transaction(PARAMS, refundTxBytes);
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
try {
serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
fail();
} catch (IllegalStateException e) {
}
......@@ -743,7 +743,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
if (useRefunds()) {
// Send the refund tx from client to server and get back the signature.
Transaction refund = new Transaction(PARAMS, clientV1State().getIncompleteRefundTransaction().bitcoinSerialize());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, CENT, myKey.getPubKey());
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
// This verifies that the refund can spend the multi-sig output when run.
clientV1State().provideRefundSignature(refundSig, null);
......@@ -842,7 +842,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
if (useRefunds()) {
// Send the refund tx from client to server and get back the signature.
Transaction refund = new Transaction(PARAMS, clientV1State().getIncompleteRefundTransaction().bitcoinSerialize());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, CENT, myKey.getPubKey());
assertEquals(PaymentChannelServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
// This verifies that the refund can spend the multi-sig output when run.
clientV1State().provideRefundSignature(refundSig, null);
......@@ -931,7 +931,7 @@ public class PaymentChannelStateTest extends TestWithWallet {
if (useRefunds()) {
refund = new Transaction(PARAMS, clientV1State().getIncompleteRefundTransaction().bitcoinSerialize());
// Send the refund tx from client to server and get back the signature.
byte[] refundSig = serverV1State().provideRefundTransaction(refund, myKey.getPubKey());
byte[] refundSig = serverV1State().provideRefundTransaction(refund, HALF_COIN, myKey.getPubKey());
assertEquals(PaymentChannelV1ServerState.State.WAITING_FOR_MULTISIG_CONTRACT, serverState.getState());
// This verifies that the refund can spend the multi-sig output when run.
clientV1State().provideRefundSignature(refundSig, null);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment