/** * If a handler is connected, call its {@link org.bitcoinj.protocols.channels.PaymentChannelServer#close()} * method thus disconnecting the TCP connection. */ synchronized void closeConnectedHandler() { if (connectedHandler != null) connectedHandler.close(); }
@GuardedBy("lock") private void receiveCloseMessage() throws InsufficientMoneyException { log.info("Got CLOSE message, closing channel"); if (state != null) { settlePayment(CloseReason.CLIENT_REQUESTED_CLOSE); } else { conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE); } }
switch (msg.getType()) { case CLIENT_VERSION: receiveVersionMessage(msg); return; case PROVIDE_REFUND: receiveRefundMessage(msg); return; case PROVIDE_CONTRACT: receiveContractMessage(msg); return; case UPDATE_PAYMENT: checkState(step == InitStep.CHANNEL_OPEN && msg.hasUpdatePayment()); receiveUpdatePaymentMessage(msg.getUpdatePayment(), true); return; case CLOSE: receiveCloseMessage(); return; case ERROR: default: final String errorText = "Got unknown message type or type that doesn't apply to servers."; error(errorText, Protos.Error.ErrorCode.SYNTAX_ERROR, CloseReason.REMOTE_SENT_INVALID_MESSAGE); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (ValueOutOfRangeException e) { log.error("Caught value out of range exception handling message from client", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (InsufficientMoneyException e) { log.error("Caught insufficient money exception handling message from client", e);
majorVersion = clientVersion.getMajor(); if (!SERVER_VERSIONS.containsKey(majorVersion)) { error("This server needs one of protocol versions " + SERVER_VERSIONS.keySet() + " , client offered " + majorVersion, Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); return; if (existingHandler != this) { log.warn(" ... and that channel is already in use, disconnecting other user."); existingHandler.close(); storedServerChannel.setConnectedHandler(this, true); wallet.freshReceiveKey(); expireTime = Utils.currentTimeSeconds() + truncateTimeWindow(clientVersion.getTimeWindowSecs()); switch (majorVersion) { case 1: break; default: error("Protocol version " + majorVersion + " not supported", Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); break;
state.storeChannelInWallet(PaymentChannelServer.this); try { receiveUpdatePaymentMessage(providedContract.getInitialPayment(), false /* no ack msg */); } catch (VerificationException e) { log.error("Initial payment failed to verify", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (ValueOutOfRangeException e) { log.error("Initial payment value was out of range", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (InsufficientMoneyException e) { error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return;
@Test public void shouldAcceptDefaultTimeWindow() { final TwoWayChannelMessage message = createClientVersionMessage(); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test(expected = IllegalArgumentException.class) public void shouldNotAllowTimeWindowLessThan2h() { dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return 40000; } @Override public long getMinTimeWindow() { return 7199; } }, connection); }
pair.server.connectionOpen(); PaymentChannelClient client = new PaymentChannelClient(wallet, myKey, COIN, someServerId, null, clientChannelProperties, pair.clientRecorder); PaymentChannelServer server = pair.server; client.connectionOpen(); server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.CLIENT_VERSION)); client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.SERVER_VERSION)); final Protos.TwoWayChannelMessage initiateMsg = pair.serverRecorder.checkNextMsg(MessageType.INITIATE); client.receiveMessage(initiateMsg); if (useRefunds()) { server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_REFUND)); client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.RETURN_REFUND)); server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.PROVIDE_CONTRACT)); broadcasts.take(); pair.serverRecorder.checkTotalPayment(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE); server.receiveMessage(pair.clientRecorder.checkNextMsg(MessageType.UPDATE_PAYMENT)); assertEquals(amount, ((ChannelTestUtils.UpdatePair)pair.serverRecorder.q.take()).amount); server.close(); server.connectionClosed(); client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.PAYMENT_ACK)); client.receiveMessage(pair.serverRecorder.checkNextMsg(MessageType.CLOSE)); pair.server.connectionOpen(); pair.server.receiveMessage(Protos.TwoWayChannelMessage.newBuilder() .setType(MessageType.CLIENT_VERSION) .setClientVersion(Protos.ClientVersion.newBuilder()
switch (msg.getType()) { case CLIENT_VERSION: receiveVersionMessage(msg); return; case PROVIDE_REFUND: receiveRefundMessage(msg); return; case PROVIDE_CONTRACT: receiveContractMessage(msg); return; case UPDATE_PAYMENT: checkState(step == InitStep.CHANNEL_OPEN && msg.hasUpdatePayment()); receiveUpdatePaymentMessage(msg.getUpdatePayment(), true); return; case CLOSE: receiveCloseMessage(); return; case ERROR: default: final String errorText = "Got unknown message type or type that doesn't apply to servers."; error(errorText, Protos.Error.ErrorCode.SYNTAX_ERROR, CloseReason.REMOTE_SENT_INVALID_MESSAGE); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (ValueOutOfRangeException e) { log.error("Caught value out of range exception handling message from client", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (InsufficientMoneyException e) { log.error("Caught insufficient money exception handling message from client", e);
majorVersion = clientVersion.getMajor(); if (!SERVER_VERSIONS.containsKey(majorVersion)) { error("This server needs one of protocol versions " + SERVER_VERSIONS.keySet() + " , client offered " + majorVersion, Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); return; if (existingHandler != this) { log.warn(" ... and that channel is already in use, disconnecting other user."); existingHandler.close(); storedServerChannel.setConnectedHandler(this, true); wallet.freshReceiveKey(); expireTime = Utils.currentTimeSeconds() + truncateTimeWindow(clientVersion.getTimeWindowSecs()); switch (majorVersion) { case 1: break; default: error("Protocol version " + majorVersion + " not supported", Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); break;
state.storeChannelInWallet(PaymentChannelServer.this); try { receiveUpdatePaymentMessage(providedContract.getInitialPayment(), false /* no ack msg */); } catch (VerificationException e) { log.error("Initial payment failed to verify", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (ValueOutOfRangeException e) { log.error("Initial payment value was out of range", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (InsufficientMoneyException e) { error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return;
@Test public void shouldAllowExactTimeWindow() { final TwoWayChannelMessage message = createClientVersionMessage(); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<>(); connection.sendToClient(capture(initiateCapture)); replay(connection); final int expire = 24 * 60 * 60 - 60; // This the default defined in paymentchannel.proto dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return expire; } @Override public long getMinTimeWindow() { return expire; } }, connection); dut.connectionOpen(); long expectedExpire = Utils.currentTimeSeconds() + expire; dut.receiveMessage(message); assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
@Test(expected = IllegalArgumentException.class) public void shouldNotAllowNegativeTimeWindow() { dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return 40000; } @Override public long getMinTimeWindow() { return 40001; } }, connection); }
switch (msg.getType()) { case CLIENT_VERSION: receiveVersionMessage(msg); return; case PROVIDE_REFUND: receiveRefundMessage(msg); return; case PROVIDE_CONTRACT: receiveContractMessage(msg); return; case UPDATE_PAYMENT: checkState(step == InitStep.CHANNEL_OPEN && msg.hasUpdatePayment()); receiveUpdatePaymentMessage(msg.getUpdatePayment(), true); return; case CLOSE: receiveCloseMessage(); return; case ERROR: default: final String errorText = "Got unknown message type or type that doesn't apply to servers."; error(errorText, Protos.Error.ErrorCode.SYNTAX_ERROR, CloseReason.REMOTE_SENT_INVALID_MESSAGE); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (ValueOutOfRangeException e) { log.error("Caught value out of range exception handling message from client", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); } catch (InsufficientMoneyException e) { log.error("Caught insufficient money exception handling message from client", e);
majorVersion = clientVersion.getMajor(); if (!SERVER_VERSIONS.containsKey(majorVersion)) { error("This server needs one of protocol versions " + SERVER_VERSIONS.keySet() + " , client offered " + majorVersion, Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); return; if (existingHandler != this) { log.warn(" ... and that channel is already in use, disconnecting other user."); existingHandler.close(); storedServerChannel.setConnectedHandler(this, true); wallet.freshReceiveKey(); expireTime = Utils.currentTimeSeconds() + truncateTimeWindow(clientVersion.getTimeWindowSecs()); switch (majorVersion) { case 1: break; default: error("Protocol version " + majorVersion + " not supported", Protos.Error.ErrorCode.NO_ACCEPTABLE_VERSION, CloseReason.NO_ACCEPTABLE_VERSION); break;
state.storeChannelInWallet(PaymentChannelServer.this); try { receiveUpdatePaymentMessage(providedContract.getInitialPayment(), false /* no ack msg */); } catch (VerificationException e) { log.error("Initial payment failed to verify", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (ValueOutOfRangeException e) { log.error("Initial payment value was out of range", e); error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return; } catch (InsufficientMoneyException e) { error(e.getMessage(), Protos.Error.ErrorCode.BAD_TRANSACTION, CloseReason.REMOTE_SENT_INVALID_MESSAGE); return;
@Test public void shouldTruncateTooLargeTimeWindow() { final int maxTimeWindow = 40000; final int timeWindow = maxTimeWindow + 1; final TwoWayChannelMessage message = createClientVersionMessage(timeWindow); final Capture<TwoWayChannelMessage> initiateCapture = new Capture<>(); connection.sendToClient(capture(initiateCapture)); replay(connection); dut = new PaymentChannelServer(broadcaster, wallet, Coin.CENT, new PaymentChannelServer.DefaultServerChannelProperties(){ @Override public long getMaxTimeWindow() { return maxTimeWindow; } @Override public long getMinTimeWindow() { return 20000; } }, connection); dut.connectionOpen(); dut.receiveMessage(message); long expectedExpire = Utils.currentTimeSeconds() + maxTimeWindow; assertServerVersion(); assertExpireTime(expectedExpire, initiateCapture); }
public ServerHandler(final SocketAddress address, final int timeoutSeconds) { paymentChannelManager = new PaymentChannelServer(broadcaster, wallet, minAcceptedChannelSize, new PaymentChannelServer.ServerConnection() { @Override public void sendToClient(Protos.TwoWayChannelMessage msg) { socketProtobufHandler.write(msg);
@GuardedBy("lock") private void receiveCloseMessage() throws InsufficientMoneyException { log.info("Got CLOSE message, closing channel"); if (state != null) { settlePayment(CloseReason.CLIENT_REQUESTED_CLOSE); } else { conn.destroyConnection(CloseReason.CLIENT_REQUESTED_CLOSE); } }
/** * If a handler is connected, call its {@link org.bitcoinj.protocols.channels.PaymentChannelServer#close()} * method thus disconnecting the TCP connection. */ synchronized void closeConnectedHandler() { if (connectedHandler != null) connectedHandler.close(); }