diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgent.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgent.java index b3098a88..c6869def 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgent.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgent.java @@ -114,11 +114,25 @@ public void processResponse(Message message) { handleReplyTxIds((ReplyTxIds) message); } else if (message instanceof ReplyTxs) { handleReplyTxs((ReplyTxs) message); + } else if (message instanceof MsgDone) { + handleDone(); } else { log.warn("Unexpected message type: {}", message.getClass().getSimpleName()); } } + /** + * The client terminated the tx-submission protocol with MsgDone. The state machine has already + * advanced to {@link TxSubmissionState#Done} (see {@link TxSubmissionState}), so the inbound + * handler stops routing to this agent and the session can be torn down; a fresh agent is created + * when the peer reconnects. Drop any in-flight bookkeeping so nothing leaks. + */ + private void handleDone() { + log.info("Received MsgDone from client - tx-submission protocol terminated, agent is Done"); + outstandingTxIds.clear(); + pendingRequest = null; + } + private void handleInit() { log.info("Received Init message from client - transitioning to Idle state"); diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionState.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionState.java index fab5bd1a..4ca74521 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionState.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionState.java @@ -2,6 +2,7 @@ import com.bloxbean.cardano.yaci.core.protocol.Message; import com.bloxbean.cardano.yaci.core.protocol.State; +import com.bloxbean.cardano.yaci.core.protocol.txsubmission.messges.MsgDone; import com.bloxbean.cardano.yaci.core.protocol.txsubmission.messges.ReplyTxIds; import com.bloxbean.cardano.yaci.core.protocol.txsubmission.messges.ReplyTxs; import com.bloxbean.cardano.yaci.core.protocol.txsubmission.messges.RequestTxIds; @@ -44,6 +45,8 @@ public boolean hasAgency(boolean isClient) { public State nextState(Message message) { if (message instanceof ReplyTxIds) return Idle; + else if (message instanceof MsgDone) + return Done; else return this; } diff --git a/core/src/test/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgentTest.java b/core/src/test/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgentTest.java index 445a4a10..027f10ab 100644 --- a/core/src/test/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgentTest.java +++ b/core/src/test/java/com/bloxbean/cardano/yaci/core/protocol/txsubmission/TxSubmissionServerAgentTest.java @@ -332,6 +332,35 @@ void testEmptyReplyTxIds() { assertEquals(0, requestTxIds.getAckTxIds()); // No transactions to acknowledge } + @Test + void testMsgDoneFromTxIdsBlockingTransitionsToDone() { + // Move to Idle, then have the server send a blocking RequestTxIds so we land in TxIdsBlocking + agent.receiveResponse(new Init()); + assertEquals(TxSubmissionState.Idle, agent.getCurrentState()); + + agent.sendRequest(new RequestTxIds(true, (short) 0, (short) 10)); + assertEquals(TxSubmissionState.TxIdsBlocking, agent.getCurrentState()); + assertFalse(agent.isDone()); + + // Client terminates the protocol with MsgDone while we are blocking-waiting. + // Before the fix the agent stayed wedged in TxIdsBlocking forever; now it must reach Done. + agent.receiveResponse(new MsgDone()); + + assertEquals(TxSubmissionState.Done, agent.getCurrentState()); + assertTrue(agent.isDone()); + assertEquals(0, agent.getOutstandingTxCount()); + } + + @Test + void testMsgDoneStateTransition() { + // Per the node-to-node tx-submission spec, MsgDone is only legal (and only handled) + // in TxIdsBlocking. The other client-agency states are not part of the spec for MsgDone, + // so they must ignore it and stay put rather than transition to Done. + assertEquals(TxSubmissionState.Done, TxSubmissionState.TxIdsBlocking.nextState(new MsgDone())); + assertEquals(TxSubmissionState.TxIdsNonBlocking, TxSubmissionState.TxIdsNonBlocking.nextState(new MsgDone())); + assertEquals(TxSubmissionState.Txs, TxSubmissionState.Txs.nextState(new MsgDone())); + } + @Test void testConfigurationValidation() { // Valid config should not log warnings