From a836d14fbdf8804526102e617413aef8bd11a2dd Mon Sep 17 00:00:00 2001 From: djmil Date: Wed, 13 Sep 2023 18:54:19 +0200 Subject: [PATCH] GamePrposal: Accept to return GameBoard --- .../cordaclient/CordaClient.java | 2 +- .../GameProposalCommandAcceptRes.java | 4 +- .../cordaclient/CordaClientTest.java | 16 +++---- .../contracts/GameBoardCommand.java | 10 ++++ .../cordacheckers/gameboard/ListFlow.java | 4 +- .../gameproposal/CommandFlow.java | 48 +++++++++++++------ 6 files changed, 55 insertions(+), 29 deletions(-) diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java index 43c2560..7476243 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java @@ -150,7 +150,7 @@ public class CordaClient { return actionResult.successStatus(); } - public UUID gameProposalAccept( // TODO shall return newGameBoard + public GameBoard gameProposalAccept( HoldingIdentity myHoldingIdentity, UUID gameProposalUuid ) { diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/GameProposalCommandAcceptRes.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/GameProposalCommandAcceptRes.java index 45c19ce..c4b1d3b 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/GameProposalCommandAcceptRes.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/GameProposalCommandAcceptRes.java @@ -1,7 +1,7 @@ package djmil.cordacheckers.cordaclient.dao.flow.arguments; -import java.util.UUID; +import djmil.cordacheckers.cordaclient.dao.GameBoard; -public record GameProposalCommandAcceptRes(UUID successStatus, String transactionId, String failureStatus) { +public record GameProposalCommandAcceptRes(GameBoard successStatus, String transactionId, String failureStatus) { } diff --git a/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java b/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java index 9de5fe0..0b35701 100644 --- a/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java +++ b/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java @@ -143,16 +143,16 @@ public class CordaClientTest { gpUuid); }); - final UUID newGameBoardId = cordaClient.gameProposalAccept( + final GameBoard gbState = cordaClient.gameProposalAccept( holdingIdentityResolver.getByUsername(gpAcquier), gpUuid); - System.out.println("New GameBoard UUID "+newGameBoardId); + System.out.println("New GameBoard UUID "+gbState); List gbListIssuer = cordaClient.gameBoardList( holdingIdentityResolver.getByUsername(gpIssuer)); - GameBoard gbAlice = findByUuid(gbListIssuer, newGameBoardId); + GameBoard gbAlice = findByUuid(gbListIssuer, gbState.id()); assertThat(gbAlice).isNotNull(); assertThat(gbAlice.opponentName()).isEqualToIgnoringCase(gpAcquier); assertThat(gbAlice.opponentColor()).isEqualByComparingTo(gpAcquierColor); @@ -162,7 +162,7 @@ public class CordaClientTest { List gbListAcquier = cordaClient.gameBoardList( holdingIdentityResolver.getByUsername(gpAcquier)); - GameBoard bgBob = findByUuid(gbListAcquier, newGameBoardId); + GameBoard bgBob = findByUuid(gbListAcquier, gbState.id()); assertThat(bgBob).isNotNull(); assertThat(bgBob.opponentName()).isEqualToIgnoringCase(gpIssuer); assertThat(bgBob.opponentColor()).isEqualByComparingTo(Piece.Color.BLACK); @@ -196,14 +196,14 @@ public class CordaClientTest { System.out.println("New GameProposal UUID "+ gpUuid); - final UUID newGameBoardId = cordaClient.gameProposalAccept( + final GameBoard gbState = cordaClient.gameProposalAccept( hiBob, gpUuid ); - System.out.println("New GameBoard UUID "+ newGameBoardId); + System.out.println("New GameBoard UUID "+ gbState.id()); - GameBoard gbSurrender = cordaClient.gameBoardCommand( - hiBob, newGameBoardId, + final GameBoard gbSurrender = cordaClient.gameBoardCommand( + hiBob, gbState.id(), new GameBoardCommand(GameBoardCommand.Type.SURRENDER) ); diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java index 51f9640..d28b8af 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java @@ -2,10 +2,14 @@ package djmil.cordacheckers.contracts; import java.util.List; +import net.corda.v5.base.annotations.ConstructorForDeserialization; +import net.corda.v5.base.annotations.CordaSerializable; import net.corda.v5.base.exceptions.CordaRuntimeException; import net.corda.v5.ledger.utxo.Command; public class GameBoardCommand implements Command { + + @CordaSerializable public static enum Type { MOVE, SURRENDER, @@ -23,6 +27,12 @@ public class GameBoardCommand implements Command { this.move = null; } + @ConstructorForDeserialization + public GameBoardCommand(Type type, List move) { + this.type = type; + this.move = move; + } + public GameBoardCommand(Type type) { if (type == Type.MOVE) throw new CordaRuntimeException (BAD_ACTIONMOVE_CONSTRUCTOR); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java index 41ceac2..7b4b846 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java @@ -67,9 +67,7 @@ public class ListFlow implements ClientStartableFlow { final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService .findLedgerTransaction(trxId); - var newGbView = new GameBoardView(myName, utxoGameBoard); - - return newGbView; + return new GameBoardView(myName, utxoGameBoard); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java index 61cd069..e5d3aa1 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java @@ -6,7 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import djmil.cordacheckers.FlowResult; +import djmil.cordacheckers.contracts.GameBoardCommand; import djmil.cordacheckers.contracts.GameProposalCommand; +import djmil.cordacheckers.gameboard.GameBoardView; import djmil.cordacheckers.states.GameBoardState; import djmil.cordacheckers.states.GameProposalState; import net.corda.v5.application.flows.ClientRequestBody; @@ -14,10 +16,13 @@ import net.corda.v5.application.flows.ClientStartableFlow; import net.corda.v5.application.flows.CordaInject; import net.corda.v5.application.flows.FlowEngine; import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.crypto.SecureHash; import net.corda.v5.ledger.utxo.StateAndRef; import net.corda.v5.ledger.utxo.UtxoLedgerService; +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder; @@ -32,11 +37,14 @@ public class CommandFlow implements ClientStartableFlow { public JsonMarshallingService jsonMarshallingService; @CordaInject - public UtxoLedgerService ledgerService; + public UtxoLedgerService utxoLedgerService; @CordaInject public FlowEngine flowEngine; + @CordaInject + public MemberLookup memberLookup; + @Override @Suspendable public String call(ClientRequestBody requestBody) { @@ -44,17 +52,17 @@ public class CommandFlow implements ClientStartableFlow { final CommandFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CommandFlowArgs.class); final GameProposalCommand command = args.getCommand(); - final StateAndRef utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid()); + final StateAndRef gpStateAndRef = findUnconsumedGameProposalState(args.getGameProposalUuid()); - final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal); + final UtxoSignedTransaction trxCandidate = prepareSignedTransaction(command, gpStateAndRef); final SecureHash trxId = this.flowEngine - .subFlow( new CommitSubFlow(trx, command.getRespondent(utxoGameProposal)) ); + .subFlow( new CommitSubFlow(trxCandidate, command.getRespondent(gpStateAndRef)) ); if (command == GameProposalCommand.ACCEPT) { - GameBoardState newGb = (GameBoardState)trx.getOutputStateAndRefs().get(0).getState().getContractState(); + final GameBoardView gbView = prepareGameBoardView(trxId); - return new FlowResult(newGb.getId(), trxId) + return new FlowResult(gbView, trxId) .toJsonEncodedString(jsonMarshallingService); } @@ -69,16 +77,16 @@ public class CommandFlow implements ClientStartableFlow { } @Suspendable - private StateAndRef findUnconsumedGameProposalState (UUID gameProposalUuid) { + private StateAndRef findUnconsumedGameProposalState (UUID gpUuid) { /* * Get list of all unconsumed aka 'active' GameProposalStates, then filter by UUID. * Note, this is an inefficient way to perform this operation if there are a large * number of 'active' GameProposals exists in storage. */ - return this.ledgerService + return this.utxoLedgerService .findUnconsumedStatesByType(GameProposalState.class) .stream() - .filter(sar -> sar.getState().getContractState().getId().equals(gameProposalUuid)) + .filter(sar -> sar.getState().getContractState().getId().equals(gpUuid)) .reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);}) .get(); } @@ -86,18 +94,18 @@ public class CommandFlow implements ClientStartableFlow { @Suspendable private UtxoSignedTransaction prepareSignedTransaction( GameProposalCommand command, - StateAndRef utxoGameProposal + StateAndRef gpStateAndRef ) { - UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder() - .setNotary(utxoGameProposal.getState().getNotaryName()) + UtxoTransactionBuilder trxBuilder = utxoLedgerService.createTransactionBuilder() + .setNotary(gpStateAndRef.getState().getNotaryName()) .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(utxoGameProposal.getRef()) + .addInputState(gpStateAndRef.getRef()) .addCommand(command) - .addSignatories(utxoGameProposal.getState().getContractState().getParticipants()); + .addSignatories(gpStateAndRef.getState().getContractState().getParticipants()); if (command == GameProposalCommand.ACCEPT) { trxBuilder = trxBuilder - .addOutputState(new GameBoardState(utxoGameProposal)); + .addOutputState(new GameBoardState(gpStateAndRef)); //A state cannot be both an input and a reference input in the same transaction //.addReferenceState(utxoGameProposal.getRef()); } @@ -105,4 +113,14 @@ public class CommandFlow implements ClientStartableFlow { return trxBuilder.toSignedTransaction(); } + @Suspendable + private GameBoardView prepareGameBoardView(SecureHash gbUtxoTrxId) { + final MemberX500Name myName = memberLookup.myInfo().getName(); + + final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService + .findLedgerTransaction(gbUtxoTrxId); + + return new GameBoardView(myName, utxoGameBoard); + } + }