org.bitcoin.paymentchannel.Protos Java Examples

The following examples show how to use org.bitcoin.paymentchannel.Protos. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PaymentChannelClient.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@GuardedBy("lock")
private void receiveClose(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(lock.isHeldByCurrentThread());
    if (msg.hasSettlement()) {
        Transaction settleTx = wallet.getParams().getDefaultSerializer().makeTransaction(msg.getSettlement().getTx().toByteArray());
        log.info("CLOSE message received with settlement tx {}", settleTx.getHash());
        // TODO: set source
        if (state != null && state().isSettlementTransaction(settleTx)) {
            // 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(settleTx, null);
        }
    } else {
        log.info("CLOSE message received without settlement tx");
    }
    if (step == InitStep.WAITING_FOR_CHANNEL_CLOSE)
        conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE);
    else
        conn.destroyConnection(CloseReason.SERVER_REQUESTED_CLOSE);
    step = InitStep.CHANNEL_CLOSED;
}
 
Example #2
Source File: PaymentChannelServer.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@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.getSettlementBuilder().setTx(ByteString.copyFrom(result.unsafeBitcoinSerialize()));
        log.info("Sending CLOSE back with broadcast settlement tx.");
    } else {
        log.info("Sending CLOSE back without broadcast settlement tx.");
    }
    conn.sendToClient(msg.build());
    conn.destroyConnection(clientRequestedClose);
}
 
Example #3
Source File: PaymentChannelClient.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
private void receivePaymentAck(Protos.PaymentAck paymentAck) {
    SettableFuture<PaymentIncrementAck> future;
    Coin value;

    lock.lock();
    try {
        if (increasePaymentFuture == null)
            return;
        checkNotNull(increasePaymentFuture, "Server sent a PAYMENT_ACK with no outstanding payment");
        log.info("Received a PAYMENT_ACK from the server");
        future = increasePaymentFuture;
        value = lastPaymentActualAmount;
    } finally {
        lock.unlock();
    }

    // Ensure the future runs without the client lock held.
    future.set(new PaymentIncrementAck(value, paymentAck.getInfo()));
}
 
Example #4
Source File: ChannelConnectionTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testServerErrorHandling_killSocketOnError() throws Exception {
    // Make sure the server closes the socket on ERROR
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    PaymentChannelServer server = pair.server;
    server.connectionOpen();
    client.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE));
    server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.ERROR)
            .setError(Protos.Error.newBuilder().setCode(Protos.Error.ErrorCode.TIMEOUT))
            .build());
    assertEquals(CloseReason.REMOTE_SENT_ERROR, pair.serverRecorder.q.take());
}
 
Example #5
Source File: PaymentChannelServer.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@GuardedBy("lock")
private void receiveRefundMessage(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(majorVersion == 1);
    checkState(step == InitStep.WAITING_ON_UNSIGNED_REFUND && msg.hasProvideRefund());
    log.info("Got refund transaction, returning signature");

    Protos.ProvideRefund providedRefund = msg.getProvideRefund();
    state = new PaymentChannelV1ServerState(broadcaster, wallet, myKey, expireTime);
    // 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()),
                    providedRefund.getMultisigKey().toByteArray());

    step = InitStep.WAITING_ON_CONTRACT;

    Protos.ReturnRefund.Builder returnRefundBuilder = Protos.ReturnRefund.newBuilder()
            .setSignature(ByteString.copyFrom(signature));

    conn.sendToClient(Protos.TwoWayChannelMessage.newBuilder()
            .setReturnRefund(returnRefundBuilder)
            .setType(Protos.TwoWayChannelMessage.MessageType.RETURN_REFUND)
            .build());
}
 
Example #6
Source File: ChannelConnectionTest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testServerErrorHandling_killSocketOnError() throws Exception {
    // Make sure the server closes the socket on ERROR
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    PaymentChannelServer server = pair.server;
    server.connectionOpen();
    client.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE));
    server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.ERROR)
            .setError(Protos.Error.newBuilder().setCode(Protos.Error.ErrorCode.TIMEOUT))
            .build());
    assertEquals(CloseReason.REMOTE_SENT_ERROR, pair.serverRecorder.q.take());
}
 
Example #7
Source File: ChannelConnectionTest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testBadResumeHash() throws InterruptedException {
    // Check that server-side will reject incorrectly formatted hashes. If anything goes wrong with session resume,
    // then the server will start the opening of a new channel automatically, so we expect to see INITIATE here.
    ChannelTestUtils.RecordingPair srv =
            ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    srv.server.connectionOpen();
    srv.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.CLIENT_VERSION)
            .setClientVersion(Protos.ClientVersion.newBuilder()
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[]{0x00, 0x01}))
                    .setMajor(CLIENT_MAJOR_VERSION).setMinor(42))
            .build());

    srv.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION);
    srv.serverRecorder.checkNextMsg(MessageType.INITIATE);
    assertTrue(srv.serverRecorder.q.isEmpty());
}
 
Example #8
Source File: ChannelConnectionTest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testClientTimeWindowUnacceptable() throws Exception {
    // Tests that clients reject too large time windows
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster, 100);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds() + 60 * 60 * 48)
                    .setMinAcceptedChannelSize(100)
                    .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey()))
                    .setMinPayment(Transaction.MIN_NONDUST_OUTPUT.value))
            .setType(MessageType.INITIATE).build());

    pair.clientRecorder.checkNextMsg(MessageType.ERROR);
    assertEquals(CloseReason.TIME_WINDOW_UNACCEPTABLE, pair.clientRecorder.q.take());
    // Double-check that we cant do anything that requires an open channel
    try {
        client.incrementPayment(Coin.SATOSHI);
        fail();
    } catch (IllegalStateException e) {
    }
}
 
Example #9
Source File: ChannelConnectionTest.java    From bcm-android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testEmptyWallet() throws Exception {
    Wallet emptyWallet = new Wallet(UNITTEST);
    emptyWallet.freshReceiveKey();
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    try {
        client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
                .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds())
                        .setMinAcceptedChannelSize(CENT.value)
                        .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey()))
                        .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value))
                .setType(MessageType.INITIATE).build());
        fail();
    } catch (InsufficientMoneyException expected) {
        // This should be thrown.
    }
}
 
Example #10
Source File: PaymentChannelClient.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
private void receivePaymentAck(Protos.PaymentAck paymentAck) {
    SettableFuture<PaymentIncrementAck> future;
    Coin value;

    lock.lock();
    try {
        if (increasePaymentFuture == null) return;
        checkNotNull(increasePaymentFuture, "Server sent a PAYMENT_ACK with no outstanding payment");
        log.info("Received a PAYMENT_ACK from the server");
        future = increasePaymentFuture;
        value = lastPaymentActualAmount;
    } finally {
        lock.unlock();
    }

    // Ensure the future runs without the client lock held.
    future.set(new PaymentIncrementAck(value, paymentAck.getInfo()));
}
 
Example #11
Source File: PaymentChannelClient.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
private void receivePaymentAck(Protos.PaymentAck paymentAck) {
    SettableFuture<PaymentIncrementAck> future;
    Coin value;

    lock.lock();
    try {
        if (increasePaymentFuture == null) return;
        checkNotNull(increasePaymentFuture, "Server sent a PAYMENT_ACK with no outstanding payment");
        log.info("Received a PAYMENT_ACK from the server");
        future = increasePaymentFuture;
        value = lastPaymentActualAmount;
    } finally {
        lock.unlock();
    }

    // Ensure the future runs without the client lock held.
    future.set(new PaymentIncrementAck(value, paymentAck.getInfo()));
}
 
Example #12
Source File: ChannelConnectionTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testClientTimeWindowUnacceptable() throws Exception {
    // Tests that clients reject too large time windows
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster, 100);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds() + 60 * 60 * 48)
                    .setMinAcceptedChannelSize(100)
                    .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey()))
                    .setMinPayment(Transaction.MIN_NONDUST_OUTPUT.value))
            .setType(MessageType.INITIATE).build());

    pair.clientRecorder.checkNextMsg(MessageType.ERROR);
    assertEquals(CloseReason.TIME_WINDOW_UNACCEPTABLE, pair.clientRecorder.q.take());
    // Double-check that we cant do anything that requires an open channel
    try {
        client.incrementPayment(Coin.SATOSHI);
        fail();
    } catch (IllegalStateException e) { }
}
 
Example #13
Source File: PaymentChannelServer.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@GuardedBy("lock")
private void receiveContractMessage(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(majorVersion == 1 || majorVersion == 2);
    checkState(step == InitStep.WAITING_ON_CONTRACT && msg.hasProvideContract());
    log.info("Got contract, broadcasting and responding with CHANNEL_OPEN");
    final Protos.ProvideContract providedContract = msg.getProvideContract();

    if (majorVersion == 2) {
        state = new PaymentChannelV2ServerState(broadcaster, wallet, myKey, expireTime);
        checkState(providedContract.hasClientKey(), "ProvideContract didn't have a client key in protocol v2");
        ((PaymentChannelV2ServerState)state).provideClientKey(providedContract.getClientKey().toByteArray());
    }

    //TODO notify connection handler that timeout should be significantly extended as we wait for network propagation?
    final Transaction contract = wallet.getParams().getDefaultSerializer().makeTransaction(providedContract.getTx().toByteArray());
    step = InitStep.WAITING_ON_MULTISIG_ACCEPTANCE;
    state.provideContract(contract)
            .addListener(new Runnable() {
                @Override
                public void run() {
                    multisigContractPropogated(providedContract, contract.getHash());
                }
            }, Threading.SAME_THREAD);
}
 
Example #14
Source File: PaymentChannelServer.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@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.getSettlementBuilder().setTx(ByteString.copyFrom(result.unsafeBitcoinSerialize()));
        log.info("Sending CLOSE back with broadcast settlement tx.");
    } else {
        log.info("Sending CLOSE back without broadcast settlement tx.");
    }
    conn.sendToClient(msg.build());
    conn.destroyConnection(clientRequestedClose);
}
 
Example #15
Source File: ChannelConnectionTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testEmptyWallet() throws Exception {
    Wallet emptyWallet = new Wallet(PARAMS);
    emptyWallet.freshReceiveKey();
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    try {
        client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
                .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds())
                        .setMinAcceptedChannelSize(CENT.value)
                        .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey()))
                        .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value))
                .setType(MessageType.INITIATE).build());
        fail();
    } catch (InsufficientMoneyException expected) {
        // This should be thrown.
    }
}
 
Example #16
Source File: PaymentChannelServer.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@GuardedBy("lock")
private void receiveContractMessage(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(majorVersion == 1 || majorVersion == 2);
    checkState(step == InitStep.WAITING_ON_CONTRACT && msg.hasProvideContract());
    log.info("Got contract, broadcasting and responding with CHANNEL_OPEN");
    final Protos.ProvideContract providedContract = msg.getProvideContract();

    if (majorVersion == 2) {
        state = new PaymentChannelV2ServerState(broadcaster, wallet, myKey, expireTime);
        checkState(providedContract.hasClientKey(), "ProvideContract didn't have a client key in protocol v2");
        ((PaymentChannelV2ServerState)state).provideClientKey(providedContract.getClientKey().toByteArray());
    }

    //TODO notify connection handler that timeout should be significantly extended as we wait for network propagation?
    final Transaction contract = wallet.getParams().getDefaultSerializer().makeTransaction(providedContract.getTx().toByteArray());
    step = InitStep.WAITING_ON_MULTISIG_ACCEPTANCE;
    state.provideContract(contract)
            .addListener(new Runnable() {
                @Override
                public void run() {
                    multisigContractPropogated(providedContract, contract.getHash());
                }
            }, Threading.SAME_THREAD);
}
 
Example #17
Source File: ChannelConnectionTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testClientRefusesNonCanonicalKey() throws Exception {
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    Protos.TwoWayChannelMessage.Builder initiateMsg = Protos.TwoWayChannelMessage.newBuilder(pair.serverRecorder.checkNextMsg(MessageType.INITIATE));
    ByteString brokenKey = initiateMsg.getInitiate().getMultisigKey();
    brokenKey = ByteString.copyFrom(Arrays.copyOf(brokenKey.toByteArray(), brokenKey.size() + 1));
    initiateMsg.getInitiateBuilder().setMultisigKey(brokenKey);
    client.receiveMessage(initiateMsg.build());
    pair.clientRecorder.checkNextMsg(MessageType.ERROR);
    assertEquals(CloseReason.REMOTE_SENT_INVALID_MESSAGE, pair.clientRecorder.q.take());
}
 
Example #18
Source File: ChannelConnectionTest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testEmptyWallet() throws Exception {
    Wallet emptyWallet = new Wallet(PARAMS);
    emptyWallet.freshReceiveKey();
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelServer server = pair.server;
    PaymentChannelClient client = new PaymentChannelClient(emptyWallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    server.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    try {
        client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
                .setInitiate(Protos.Initiate.newBuilder().setExpireTimeSecs(Utils.currentTimeSeconds())
                        .setMinAcceptedChannelSize(CENT.value)
                        .setMultisigKey(ByteString.copyFrom(new ECKey().getPubKey()))
                        .setMinPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.value))
                .setType(MessageType.INITIATE).build());
        fail();
    } catch (InsufficientMoneyException expected) {
        // This should be thrown.
    }
}
 
Example #19
Source File: ChannelConnectionTest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testClientUnknownVersion() throws Exception {
    // Tests client rejects unknown version
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    client.connectionOpen();
    pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION);
    client.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setServerVersion(Protos.ServerVersion.newBuilder().setMajor(-1))
            .setType(MessageType.SERVER_VERSION).build());
    pair.clientRecorder.checkNextMsg(MessageType.ERROR);
    assertEquals(CloseReason.NO_ACCEPTABLE_VERSION, pair.clientRecorder.q.take());
    // Double-check that we cant do anything that requires an open channel
    try {
        client.incrementPayment(Coin.SATOSHI);
        fail();
    } catch (IllegalStateException e) { }
}
 
Example #20
Source File: ChannelConnectionTest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testBadResumeHash() throws InterruptedException {
    // Check that server-side will reject incorrectly formatted hashes. If anything goes wrong with session resume,
    // then the server will start the opening of a new channel automatically, so we expect to see INITIATE here.
    ChannelTestUtils.RecordingPair srv =
            ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    srv.server.connectionOpen();
    srv.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.CLIENT_VERSION)
            .setClientVersion(Protos.ClientVersion.newBuilder()
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[]{0x00, 0x01}))
                    .setMajor(CLIENT_MAJOR_VERSION).setMinor(42))
            .build());

    srv.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION);
    srv.serverRecorder.checkNextMsg(MessageType.INITIATE);
    assertTrue(srv.serverRecorder.q.isEmpty());
}
 
Example #21
Source File: ChannelConnectionTest.java    From GreenBits with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testBadResumeHash() throws InterruptedException {
    // Check that server-side will reject incorrectly formatted hashes. If anything goes wrong with session resume,
    // then the server will start the opening of a new channel automatically, so we expect to see INITIATE here.
    ChannelTestUtils.RecordingPair srv =
            ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    srv.server.connectionOpen();
    srv.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.CLIENT_VERSION)
            .setClientVersion(Protos.ClientVersion.newBuilder()
                    .setPreviousChannelContractHash(ByteString.copyFrom(new byte[]{0x00, 0x01}))
                    .setMajor(CLIENT_MAJOR_VERSION).setMinor(42))
            .build());

    srv.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION);
    srv.serverRecorder.checkNextMsg(MessageType.INITIATE);
    assertTrue(srv.serverRecorder.q.isEmpty());
}
 
Example #22
Source File: ChannelConnectionTest.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@Test
public void testServerErrorHandling_killSocketOnError() throws Exception {
    // Make sure the server closes the socket on ERROR
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    PaymentChannelServer server = pair.server;
    server.connectionOpen();
    client.connectionOpen();
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE));
    server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.ERROR)
            .setError(Protos.Error.newBuilder().setCode(Protos.Error.ErrorCode.TIMEOUT))
            .build());
    assertEquals(CloseReason.REMOTE_SENT_ERROR, pair.serverRecorder.q.take());
}
 
Example #23
Source File: PaymentChannelServer.java    From green_android with GNU General Public License v3.0 6 votes vote down vote up
@GuardedBy("lock")
private void receiveRefundMessage(Protos.TwoWayChannelMessage msg) throws VerificationException {
    checkState(majorVersion == 1);
    checkState(step == InitStep.WAITING_ON_UNSIGNED_REFUND && msg.hasProvideRefund());
    log.info("Got refund transaction, returning signature");

    Protos.ProvideRefund providedRefund = msg.getProvideRefund();
    state = new PaymentChannelV1ServerState(broadcaster, wallet, myKey, expireTime);
    // 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()),
                    providedRefund.getMultisigKey().toByteArray());

    step = InitStep.WAITING_ON_CONTRACT;

    Protos.ReturnRefund.Builder returnRefundBuilder = Protos.ReturnRefund.newBuilder()
            .setSignature(ByteString.copyFrom(signature));

    conn.sendToClient(Protos.TwoWayChannelMessage.newBuilder()
            .setReturnRefund(returnRefundBuilder)
            .setType(Protos.TwoWayChannelMessage.MessageType.RETURN_REFUND)
            .build());
}
 
Example #24
Source File: PaymentChannelClient.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
/**
 * <p>Called to indicate the connection has been opened and messages can now be generated for the server.</p>
 *
 * <p>Attempts to find a channel to resume and generates a CLIENT_VERSION message for the server based on the
 * result.</p>
 */
@Override
public void connectionOpen() {
    lock.lock();
    try {
        connectionOpen = true;

        StoredPaymentChannelClientStates channels = (StoredPaymentChannelClientStates) wallet.getExtensions().get(StoredPaymentChannelClientStates.EXTENSION_ID);
        if (channels != null)
            storedChannel = channels.getUsableChannelForServerID(serverId);

        step = InitStep.WAITING_FOR_VERSION_NEGOTIATION;

        Protos.ClientVersion.Builder versionNegotiationBuilder = Protos.ClientVersion.newBuilder()
                .setMajor(versionSelector.getRequestedMajorVersion())
                .setMinor(versionSelector.getRequestedMinorVersion())
                .setTimeWindowSecs(timeWindow);

        if (storedChannel != null) {
            versionNegotiationBuilder.setPreviousChannelContractHash(ByteString.copyFrom(storedChannel.contract.getHash().getBytes()));
            log.info("Begun version handshake, attempting to reopen channel with contract hash {}", storedChannel.contract.getHash());
        } else
            log.info("Begun version handshake creating new channel");

        conn.sendToServer(Protos.TwoWayChannelMessage.newBuilder()
                .setType(Protos.TwoWayChannelMessage.MessageType.CLIENT_VERSION)
                .setClientVersion(versionNegotiationBuilder)
                .build());
    } finally {
        lock.unlock();
    }
}
 
Example #25
Source File: ChannelConnectionTest.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
private static Wallet roundTripClientWallet(Wallet wallet) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    new WalletProtobufSerializer().writeWallet(wallet, bos);
    org.bitcoinj.wallet.Protos.Wallet proto = WalletProtobufSerializer.parseToProto(new ByteArrayInputStream(bos.toByteArray()));
    StoredPaymentChannelClientStates state = new StoredPaymentChannelClientStates(null, failBroadcaster);
    return new WalletProtobufSerializer().readWallet(wallet.getParams(), new WalletExtension[] { state }, proto);
}
 
Example #26
Source File: PaymentChannelClient.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
/**
 * <p>Closes the connection, notifying the server it should settle the channel by broadcasting the most recent
 * payment transaction.</p>
 *
 * <p>Note that this only generates a CLOSE message for the server and calls
 * {@link ClientConnection#destroyConnection(CloseReason)} to settle the connection, it does not
 * actually handle connection close logic, and {@link PaymentChannelClient#connectionClosed()} must still be called
 * after the connection fully closes.</p>
 *
 * @throws IllegalStateException If the connection is not currently open (ie the CLOSE message cannot be sent)
 */
@Override
public void settle() throws IllegalStateException {
    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 settlement.");
        conn.sendToServer(Protos.TwoWayChannelMessage.newBuilder()
                .setType(Protos.TwoWayChannelMessage.MessageType.CLOSE)
                .build());
    } finally {
        lock.unlock();
    }
}
 
Example #27
Source File: PaymentChannelClient.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
@GuardedBy("lock")
private void receiveRefund(Protos.TwoWayChannelMessage refundMsg, @Nullable KeyParameter userKey) throws VerificationException {
    checkState(majorVersion == 1);
    checkState(step == InitStep.WAITING_FOR_REFUND_RETURN && refundMsg.hasReturnRefund());
    log.info("Got RETURN_REFUND message, providing signed contract");
    Protos.ReturnRefund returnedRefund = refundMsg.getReturnRefund();
    // Cast is safe since we've checked the version number
    ((PaymentChannelV1ClientState)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
    // transaction is safely in the wallet - thus we store it (this also keeps it up-to-date when we pay)
    state.storeChannelInWallet(serverId);

    Protos.ProvideContract.Builder contractMsg = Protos.ProvideContract.newBuilder()
            .setTx(ByteString.copyFrom(state.getContract().unsafeBitcoinSerialize()));
    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), userKey);
        Protos.UpdatePayment.Builder initialMsg = contractMsg.getInitialPaymentBuilder();
        initialMsg.setSignature(ByteString.copyFrom(payment.signature.encodeToBitcoin()));
        initialMsg.setClientChangeValue(state.getValueRefunded().value);
    } catch (ValueOutOfRangeException e) {
        throw new IllegalStateException(e);  // This cannot happen.
    }

    final Protos.TwoWayChannelMessage.Builder msg = Protos.TwoWayChannelMessage.newBuilder();
    msg.setProvideContract(contractMsg);
    msg.setType(Protos.TwoWayChannelMessage.MessageType.PROVIDE_CONTRACT);
    conn.sendToServer(msg.build());
}
 
Example #28
Source File: PaymentChannelClient.java    From bcm-android with GNU General Public License v3.0 5 votes vote down vote up
@GuardedBy("lock")
private void receiveRefund(Protos.TwoWayChannelMessage refundMsg, @Nullable KeyParameter userKey) throws VerificationException {
    checkState(majorVersion == 1);
    checkState(step == InitStep.WAITING_FOR_REFUND_RETURN && refundMsg.hasReturnRefund());
    log.info("Got RETURN_REFUND message, providing signed contract");
    Protos.ReturnRefund returnedRefund = refundMsg.getReturnRefund();
    // Cast is safe since we've checked the version number
    ((PaymentChannelV1ClientState) 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
    // transaction is safely in the wallet - thus we store it (this also keeps it up-to-date when we pay)
    state.storeChannelInWallet(serverId);

    Protos.ProvideContract.Builder contractMsg = Protos.ProvideContract.newBuilder()
            .setTx(ByteString.copyFrom(state.getContract().unsafeBitcoinSerialize()));
    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), userKey);
        Protos.UpdatePayment.Builder initialMsg = contractMsg.getInitialPaymentBuilder();
        initialMsg.setSignature(ByteString.copyFrom(payment.signature.encodeToBitcoin()));
        initialMsg.setClientChangeValue(state.getValueRefunded().value);
    } catch (ValueOutOfRangeException e) {
        throw new IllegalStateException(e);  // This cannot happen.
    }

    final Protos.TwoWayChannelMessage.Builder msg = Protos.TwoWayChannelMessage.newBuilder();
    msg.setProvideContract(contractMsg);
    msg.setType(Protos.TwoWayChannelMessage.MessageType.PROVIDE_CONTRACT);
    conn.sendToServer(msg.build());
}
 
Example #29
Source File: PaymentChannelClient.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Increments the total value which we pay the server. Note that the amount of money sent may not be the same as the
 * amount of money actually requested. It can be larger if the amount left over in the channel would be too small to
 * be accepted by the Bitcoin network. ValueOutOfRangeException will be thrown, however, if there's not enough money
 * left in the channel to make the payment at all. Only one payment can be in-flight at once. You have to ensure
 * you wait for the previous increase payment future to complete before incrementing the payment again.
 *
 * @param size How many satoshis to increment the payment by (note: not the new total).
 * @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, @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();

        PaymentChannelV1ClientState.IncrementedPayment payment = state().incrementPaymentBy(size, userKey);
        Protos.UpdatePayment.Builder updatePaymentBuilder = Protos.UpdatePayment.newBuilder()
                .setSignature(ByteString.copyFrom(payment.signature.encodeToBitcoin()))
                .setClientChangeValue(state.getValueRefunded().value);
        if (info != null) updatePaymentBuilder.setInfo(info);

        increasePaymentFuture = SettableFuture.create();
        increasePaymentFuture.addListener(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                increasePaymentFuture = null;
                lock.unlock();
            }
        }, MoreExecutors.directExecutor());

        conn.sendToServer(Protos.TwoWayChannelMessage.newBuilder()
                .setUpdatePayment(updatePaymentBuilder)
                .setType(Protos.TwoWayChannelMessage.MessageType.UPDATE_PAYMENT)
                .build());
        lastPaymentActualAmount = payment.amount;
        return increasePaymentFuture;
    } finally {
        lock.unlock();
    }
}
 
Example #30
Source File: ChannelConnectionTest.java    From bcm-android with GNU General Public License v3.0 5 votes vote down vote up
@Test
public void testServerErrorHandling_badTransaction() throws Exception {
    if (!useRefunds()) {
        // This test only applies to versions with refunds
        return;
    }
    // Gives the server crap and checks proper error responses are sent.
    ChannelTestUtils.RecordingPair pair = ChannelTestUtils.makeRecorders(serverWallet, mockBroadcaster);
    PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, Sha256Hash.ZERO_HASH, null, clientChannelProperties, pair.clientRecorder);
    PaymentChannelServer server = pair.server;
    server.connectionOpen();
    client.connectionOpen();

    // Make sure we get back a BAD_TRANSACTION if we send a bogus refund transaction.
    server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION));
    client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.INITIATE));
    Protos.TwoWayChannelMessage msg = pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND);
    server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder()
            .setType(MessageType.PROVIDE_REFUND)
            .setProvideRefund(
                    Protos.ProvideRefund.newBuilder(msg.getProvideRefund())
                            .setMultisigKey(ByteString.EMPTY)
                            .setTx(ByteString.EMPTY)
            ).build());
    final Protos.TwoWayChannelMessage errorMsg = pair.serverRecorder.checkNextMsg(MessageType.ERROR);
    assertEquals(Protos.Error.ErrorCode.BAD_TRANSACTION, errorMsg.getError().getCode());
}