diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java index 83e12f4..85d30d3 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java @@ -21,15 +21,15 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract { switch (command.action) { case GAME_PROPOSAL_ACCEPT: - GameCommand.validateGameProposalAccept(trx); + command.validateGameProposalAccept(trx); break; case GAME_BOARD_MOVE: - GameCommand.validateGameBoardMove(trx); + command.validateGameBoardMove(trx); break; case GAME_BOARD_SURRENDER: - GameCommand.validateGameBoardSurrender(trx); + command.validateGameBoardSurrender(trx); break; default: diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameCommand.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameCommand.java index caa87fd..ceeacfb 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameCommand.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameCommand.java @@ -1,5 +1,11 @@ package djmil.cordacheckers.contracts; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleInputState; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleOutputState; + +import java.util.LinkedList; +import java.util.List; + import djmil.cordacheckers.states.GameBoardState; import djmil.cordacheckers.states.GameProposalState; import djmil.cordacheckers.states.GameResultState; @@ -11,13 +17,6 @@ import net.corda.v5.ledger.utxo.Command; import net.corda.v5.ledger.utxo.StateAndRef; import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction; -import java.lang.reflect.Array; -import java.util.LinkedList; -import java.util.List; - -import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleInputState; -import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleOutputState; - public class GameCommand implements Command { @CordaSerializable public static enum Action { @@ -78,48 +77,49 @@ public class GameCommand implements Command { /* * Session initiator/respondent */ - - public MemberX500Name getObserver(StateAndRef gameStateSar) { + public MemberX500Name getCounterparty(StateAndRef gameStateSar) { final GameState gameState = gameStateSar.getState().getContractState(); - return gameState.getCounterpartyName(getActor(gameState)); + return gameState.getOpponentName(getInitiator(gameState)); } - public MemberX500Name getActor(StateAndRef gameStateSar) { - return getActor(gameStateSar.getState().getContractState()); + public MemberX500Name getCounterparty(GameState gameState) { + return gameState.getOpponentName(getInitiator(gameState)); } - public MemberX500Name getObserver(GameState gameState) { - return gameState.getCounterpartyName(getActor(gameState)); + public MemberX500Name getInitiator(StateAndRef gameStateSar) { + return getInitiator(gameStateSar.getState().getContractState()); } - public MemberX500Name getActor(GameState gameState) { + public MemberX500Name getInitiator(GameState gameState) { switch (action) { case GAME_PROPOSAL_CREATE: case GAME_PROPOSAL_CANCEL: - if (gameState instanceof GameProposalState) - return ((GameProposalState)gameState).getIssuer(); + if (gameState instanceof GameProposalState) + return ((GameProposalState)gameState).getIssuerName(); break; case GAME_PROPOSAL_REJECT: if (gameState instanceof GameProposalState) - return ((GameProposalState)gameState).getAcquier(); + return ((GameProposalState)gameState).getAcquierName(); break; case GAME_PROPOSAL_ACCEPT: if (gameState instanceof GameProposalState) // <<-- Session validation perspective - return ((GameProposalState)gameState).getAcquier(); + return ((GameProposalState)gameState).getAcquierName(); if (gameState instanceof GameBoardState) // <<-- GameViewBuilder perspective - return ((GameBoardState)gameState).getMovePlayerName(); + return ((GameBoardState)gameState).getActivePlayerName(); + break; + + case GAME_BOARD_SURRENDER: + if (gameState instanceof GameBoardState) // <<-- Session validation perspective + return ((GameBoardState)gameState).getActivePlayerName(); + if (gameState instanceof GameResultState) // <<-- GameViewBuilder perspective + return ((GameResultState)gameState).getLooserName(); break; case GAME_BOARD_MOVE: if (gameState instanceof GameBoardState) - return ((GameBoardState)gameState).getMovePlayerName(); - break; - - case GAME_BOARD_SURRENDER: - if (gameState instanceof GameResultState) - return ((GameResultState)gameState).getOpponentName(); + return ((GameBoardState)gameState).getActivePlayerName(); break; case GAME_RESULT_CREATE: @@ -138,75 +138,79 @@ public class GameCommand implements Command { * Transaction validation */ - public static void validateGameProposalCreate(UtxoLedgerTransaction trx) { + public void validateGameProposalCreate(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE); requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE); - GameProposalState outputState = getSingleOutputState(trx, GameProposalState.class); + final GameProposalState gameProposal = getSingleOutputState(trx, GameProposalState.class); - requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR); + requireThat(gameProposal.getOpponentName(gameProposal.getIssuerName()).compareTo(gameProposal.getAcquierName()) == 0, + "GameProposal.Issuer must be either Black or White player"); } - public static void validateGameProposalAccept(UtxoLedgerTransaction trx) { + public void validateGameProposalAccept(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().size() == 1, ACCEPT_INPUT_STATE); requireThat(trx.getOutputContractStates().size() == 1, ACCEPT_OUTPUT_STATE); - GameProposalState inGameProposal = getSingleInputState(trx, GameProposalState.class); - GameBoardState outGameBoard = getSingleOutputState(trx, GameBoardState.class); + final GameProposalState inGameProposal = getSingleInputState(trx, GameProposalState.class); + final GameBoardState outGameBoard = getSingleOutputState(trx, GameBoardState.class); - requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), IN_OUT_PARTICIPANTS); + requireThat(inGameProposal.getParticipants().containsAll(outGameBoard.getParticipants()), IN_OUT_PARTICIPANTS); } - public static void validateGameProposalReject(UtxoLedgerTransaction trx) { + public void validateGameProposalReject(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE); requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE); getSingleInputState(trx, GameProposalState.class); } - public static void validateGameProposalCancel(UtxoLedgerTransaction trx) { + public void validateGameProposalCancel(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().size() == 1, CANCEL_INPUT_STATE); requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE); getSingleInputState(trx, GameProposalState.class); } - public static void validateGameBoardSurrender(UtxoLedgerTransaction trx) { + public void validateGameBoardSurrender(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().size() == 1, SURRENDER_INPUT_STATE); final var inGameBoardState = getSingleInputState(trx, GameBoardState.class); requireThat(trx.getOutputContractStates().size() == 1, SURRENDER_OUTPUT_STATE); final var outGameResultState = getSingleOutputState(trx, GameResultState.class); - List inActors = new LinkedList(List.of( - inGameBoardState.getWhitePlayerName(), - inGameBoardState.getBlackPlayerName()) + inGameBoardState.getWhitePlayer(), + inGameBoardState.getBlackPlayer()) ); List outActors = new LinkedList(List.of( - outGameResultState.getWinnerName(), - outGameResultState.getOpponentName()) + outGameResultState.getWhitePlayer(), + outGameResultState.getBlackPlayer()) ); requireThat(inActors.containsAll(outActors) && outActors.containsAll(inActors), IN_OUT_PARTICIPANTS); - final var activePlayerName = outGameResultState.getCounterpartyName(outGameResultState.getWinnerName()); - requireThat(inGameBoardState.getMovePlayerName().compareTo(activePlayerName) == 0, SURRENDER_ACTOR + "expected " +inGameBoardState.getMovePlayerName() +" actual " +activePlayerName); + final var activePlayerName = getInitiator(inGameBoardState); + requireThat(inGameBoardState.getActivePlayerName().compareTo(activePlayerName) == 0, SURRENDER_ACTOR + "expected " +inGameBoardState.getActivePlayerName() +" actual " +activePlayerName); + + final var expectedWinnerName = inGameBoardState.getOpponentName(activePlayerName); + requireThat(outGameResultState.getWinnerName().compareTo(expectedWinnerName) == 0, + "Expected winner "+expectedWinnerName.getCommonName() +", proposed winner " +outGameResultState.getWinnerName().getCommonName()); } - public static void validateGameBoardMove(UtxoLedgerTransaction trx) { + public void validateGameBoardMove(UtxoLedgerTransaction trx) { requireThat(trx.getInputContractStates().size() == 1, MOVE_INPUT_STATE); final var inGameBoardState = getSingleInputState(trx, GameBoardState.class); requireThat(trx.getOutputContractStates().size() == 1, MOVE_OUTPUT_STATE); final var outGameBoardState = getSingleOutputState(trx, GameBoardState.class); - requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameBoardState.getWhitePlayerName()) == 0, IN_OUT_PARTICIPANTS); - requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameBoardState.getBlackPlayerName()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getWhitePlayer().compareTo(outGameBoardState.getWhitePlayer()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getBlackPlayer().compareTo(outGameBoardState.getBlackPlayer()) == 0, IN_OUT_PARTICIPANTS); } - public static void validateGameResultCreate(UtxoLedgerTransaction trx) { + public void validateGameResultCreate(UtxoLedgerTransaction trx) { throw new RuntimeException("Unimplemented"); } @@ -221,7 +225,6 @@ public class GameCommand implements Command { static final String CREATE_INPUT_STATE = "Create command should have no input states"; static final String CREATE_OUTPUT_STATE = "Create command should output exactly one GameProposal state"; - static final String NON_NULL_RECIPIENT_COLOR = "GameProposal.recipientColor field can not be null"; static final String REJECT_INPUT_STATE = "Reject command should have exactly one GameProposal input state"; static final String REJECT_OUTPUT_STATE = "Reject command should have no output states"; diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java index 0cfdc36..59e9d86 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java @@ -21,19 +21,19 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract { switch (command.action) { case GAME_PROPOSAL_CREATE: - GameCommand.validateGameProposalCreate(trx); + command.validateGameProposalCreate(trx); break; case GAME_PROPOSAL_ACCEPT: - GameCommand.validateGameProposalAccept(trx); + command.validateGameProposalAccept(trx); break; case GAME_PROPOSAL_REJECT: - GameCommand.validateGameProposalReject(trx); + command.validateGameProposalReject(trx); break; case GAME_PROPOSAL_CANCEL: - GameCommand.validateGameProposalCancel(trx); + command.validateGameProposalCancel(trx); break; default: diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameResultContract.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameResultContract.java index f9bf240..0807616 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameResultContract.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameResultContract.java @@ -21,11 +21,11 @@ public class GameResultContract implements net.corda.v5.ledger.utxo.Contract { switch (command.action) { case GAME_BOARD_SURRENDER: - GameCommand.validateGameBoardSurrender(trx); + command.validateGameBoardSurrender(trx); break; case GAME_RESULT_CREATE: - GameCommand.validateGameResultCreate(trx); + command.validateGameResultCreate(trx); break; default: diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java index 33dc288..f2d951b 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java @@ -14,19 +14,13 @@ import net.corda.v5.ledger.utxo.BelongsToContract; @BelongsToContract(GameBoardContract.class) public class GameBoardState extends GameState { - - private final MemberX500Name whitePlayerName; - private final MemberX500Name blackPlayerName; - private final Piece.Color moveColor; private final Integer moveNumber; private final Map board; public GameBoardState(GameProposalState gameProposalState) { - super(gameProposalState.gameUuid, gameProposalState.message, gameProposalState.participants); - - this.whitePlayerName = gameProposalState.getWhitePlayerName(); - this.blackPlayerName = gameProposalState.getBlackPlayerName(); + super(gameProposalState.whitePlayer, gameProposalState.blackPlayer, + gameProposalState.gameUuid, gameProposalState.message, gameProposalState.participants); // Initial GameBoard state this.moveColor = Piece.Color.WHITE; @@ -34,43 +28,26 @@ public class GameBoardState extends GameState { this.board = new LinkedHashMap(initialBoard); } - public GameBoardState( - GameBoardState oldGameBoardState, Map newBoard, Piece.Color moveColor) { - super(oldGameBoardState.gameUuid, oldGameBoardState.message, oldGameBoardState.participants); - - this.whitePlayerName = oldGameBoardState.getWhitePlayerName(); - this.blackPlayerName = oldGameBoardState.getBlackPlayerName(); + public GameBoardState(GameBoardState oldGameBoardState, Map newBoard, Piece.Color moveColor) { + super(oldGameBoardState.whitePlayer, oldGameBoardState.blackPlayer, + oldGameBoardState.gameUuid, oldGameBoardState.message, oldGameBoardState.participants); - // Initial GameBoard state this.moveColor = moveColor; this.moveNumber = oldGameBoardState.getMoveNumber() +1; this.board = newBoard; } @ConstructorForDeserialization - public GameBoardState(MemberX500Name whitePlayerName, MemberX500Name blackPlayerName, + public GameBoardState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, Color moveColor, Integer moveNumber, Map board, String message, UUID gameUuid, List participants) { - super(gameUuid, message, participants); + super(whitePlayer, blackPlayer, gameUuid, message, participants); - this.whitePlayerName = whitePlayerName; - this.blackPlayerName = blackPlayerName; this.moveColor = moveColor; this.moveNumber = moveNumber; this.board = board; } - public MemberX500Name getMovePlayerName() { - switch (moveColor) { - case WHITE: - return whitePlayerName; - case BLACK: - return blackPlayerName; - default: - throw new Piece.Color.UnknownException(); - } - } - public Piece.Color getMoveColor() { return moveColor; } @@ -79,26 +56,14 @@ public class GameBoardState extends GameState { return moveNumber; } + public MemberX500Name getActivePlayerName() { + return moveColor == Piece.Color.WHITE ? whitePlayer : blackPlayer; + } + public Map getBoard() { return board; } - @Override - public MemberX500Name getWhitePlayerName() { - return whitePlayerName; - } - - @Override - public MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved { - if (whitePlayerName.compareTo(myName) == 0) - return blackPlayerName; - - if (blackPlayerName.compareTo(myName) == 0) - return whitePlayerName; - - throw new GameState.NotInvolved(myName, GameBoardState.class, this.gameUuid); - } - // TODO: move to contract public final static Map initialBoard = Map.ofEntries( // Inspired by Checkers notation rules: https://www.bobnewell.net/nucleus/checkers.php diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java index 3ef29b1..27a4bdf 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java @@ -12,50 +12,21 @@ import net.corda.v5.ledger.utxo.BelongsToContract; @BelongsToContract(GameProposalContract.class) public class GameProposalState extends GameState { - private final MemberX500Name issuer; - private final MemberX500Name acquier; - private final Piece.Color acquierColor; + private final MemberX500Name issuerName; @ConstructorForDeserialization - public GameProposalState( - MemberX500Name issuer, - MemberX500Name acquier, - Piece.Color acquierColor, - String message, - UUID gameUuid, - List participants - ) { - super(gameUuid, message, participants); - this.issuer = issuer; - this.acquier = acquier; - this.acquierColor = acquierColor; + public GameProposalState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, MemberX500Name issuerName, + UUID gameUuid, String message, List participants) { + super(whitePlayer, blackPlayer, gameUuid, message, participants); + this.issuerName = issuerName; } - public MemberX500Name getIssuer() { - return issuer; + public MemberX500Name getIssuerName() { + return issuerName; } - public MemberX500Name getAcquier() { - return acquier; - } - - public Piece.Color getAcquierColor() { - return acquierColor; - } - - public MemberX500Name getWhitePlayerName() { - return acquierColor == Piece.Color.WHITE ? getAcquier() : getIssuer(); - } - - @Override - public MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved { - if (issuer.compareTo(myName) == 0) - return acquier; - - if (acquier.compareTo(myName) == 0) - return issuer; - - throw new GameState.NotInvolved(myName, GameProposalState.class, this.gameUuid); + public MemberX500Name getAcquierName() { + return getOpponentName(issuerName); } } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java index d15e283..1a54c83 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java @@ -13,52 +13,25 @@ import net.corda.v5.ledger.utxo.BelongsToContract; public class GameResultState extends GameState { private final MemberX500Name winnerName; - private final MemberX500Name opponentName; - - private final Piece.Color winnerColor; @ConstructorForDeserialization - public GameResultState(MemberX500Name winnerName, MemberX500Name opponentName, Piece.Color winnerColor, + public GameResultState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, MemberX500Name winnerName, UUID gameUuid, String message, List participants) { - super(gameUuid, message, participants); + super(whitePlayer, blackPlayer, gameUuid, message, participants); this.winnerName = winnerName; - this.opponentName = opponentName; - this.winnerColor = winnerColor; } public GameResultState(GameBoardState gameBoardState, MemberX500Name winnerName) { - super(gameBoardState.gameUuid, null, gameBoardState.participants); - this.opponentName = gameBoardState.getCounterpartyName(winnerName); + super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants); this.winnerName = winnerName; - this.winnerColor = gameBoardState.getWhitePlayerName().compareTo(winnerName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK; } public MemberX500Name getWinnerName() { return winnerName; } - public MemberX500Name getOpponentName() { - return opponentName; - } - - public Piece.Color getWinnerColor() { - return winnerColor; - } - - @Override - public MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved { - if (winnerName.compareTo(myName) == 0) - return opponentName; - - if (opponentName.compareTo(myName) == 0) - return winnerName; - - throw new GameState.NotInvolved(myName, GameResultState.class, this.gameUuid); - } - - @Override - public MemberX500Name getWhitePlayerName() { - return winnerColor == Piece.Color.WHITE ? winnerName : opponentName; + public MemberX500Name getLooserName() { + return getOpponentName(getWinnerName()); } } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java index 3d71c6e..662cec0 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java @@ -4,30 +4,35 @@ import java.security.PublicKey; import java.util.List; import java.util.UUID; -import org.jetbrains.annotations.NotNull; - import net.corda.v5.base.annotations.CordaSerializable; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.ContractState; @CordaSerializable public abstract class GameState implements ContractState { + final MemberX500Name whitePlayer; + final MemberX500Name blackPlayer; final UUID gameUuid; final String message; final List participants; - @NotNull - public abstract MemberX500Name getWhitePlayerName(); - - @NotNull - public abstract MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved; - - GameState(UUID gameUuid, String message, List participants) { + GameState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, UUID gameUuid, + String message, List participants) { + this.whitePlayer = whitePlayer; + this.blackPlayer = blackPlayer; this.gameUuid = gameUuid; this.message = message; this.participants = participants; } + public MemberX500Name getWhitePlayer() { + return whitePlayer; + } + + public MemberX500Name getBlackPlayer() { + return blackPlayer; + } + public UUID getGameUuid() { return gameUuid; } @@ -36,18 +41,25 @@ public abstract class GameState implements ContractState { return message; } + @Override public List getParticipants() { return participants; } - public MemberX500Name getBlackPlayerName() { - return getCounterpartyName(getWhitePlayerName()); + public MemberX500Name getOpponentName(MemberX500Name playerName) throws NotInvolved { + if (playerName.compareTo(whitePlayer) == 0) + return blackPlayer; + if (playerName.compareTo(blackPlayer) == 0) + return whitePlayer; + throw new NotInvolved(playerName, this.getClass(), gameUuid); } - public Piece.Color getCounterpartyColor(MemberX500Name myName) throws NotInvolved { - final MemberX500Name opponentName = getCounterpartyName(myName); - - return getWhitePlayerName().compareTo(opponentName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK; + public Piece.Color getOpponentColor(MemberX500Name playerName) throws NotInvolved { + if (playerName.compareTo(whitePlayer) == 0) + return Piece.Color.BLACK; + if (playerName.compareTo(blackPlayer) == 0) + return Piece.Color.WHITE; + throw new NotInvolved(playerName, this.getClass(), gameUuid); } public static class NotInvolved extends RuntimeException { diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/SurrenderFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/SurrenderFlow.java index d79ab7c..0eba9cc 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/SurrenderFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/SurrenderFlow.java @@ -52,7 +52,7 @@ public class SurrenderFlow implements ClientStartableFlow{ @Suspendable @Override public String call(ClientRequestBody requestBody) { - SecureHash gameStateUtxoTrxId = null; + SecureHash utxoTrxId = null; try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_BOARD_SURRENDER); @@ -64,7 +64,7 @@ public class SurrenderFlow implements ClientStartableFlow{ final GameResultState outputState = prepareGameResultState(inputStateSar); - final UtxoSignedTransaction transactionCandidate = utxoLedgerService.createTransactionBuilder() + final UtxoSignedTransaction gameBoardSurrenderTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) .addInputState(inputStateSar.getRef()) .addOutputState(outputState) @@ -73,19 +73,19 @@ public class SurrenderFlow implements ClientStartableFlow{ .setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .toSignedTransaction(); - gameStateUtxoTrxId = this.flowEngine - .subFlow(new CommitSubFlow(transactionCandidate, command.getObserver(outputState))); + utxoTrxId = this.flowEngine + .subFlow(new CommitSubFlow(gameBoardSurrenderTrx, command.getCounterparty(inputStateSar))); final View gameStateView = this.flowEngine - .subFlow(new ViewBuilder(gameStateUtxoTrxId)); + .subFlow(new ViewBuilder(utxoTrxId)); - return new FlowResponce(gameStateView, gameStateUtxoTrxId) + return new FlowResponce(gameStateView, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); e.printStackTrace(System.out); - return new FlowResponce(e, gameStateUtxoTrxId) + return new FlowResponce(e, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } } @@ -96,7 +96,7 @@ public class SurrenderFlow implements ClientStartableFlow{ final GameBoardState gameBoard = (GameBoardState) gameState; final MemberX500Name myName = memberLookup.myInfo().getName(); - final MemberX500Name winnerName = gameBoard.getCounterpartyName(myName); + final MemberX500Name winnerName = gameBoard.getOpponentName(myName); return new GameResultState(gameBoard, winnerName); } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java index 9847bbe..2941c54 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java @@ -43,40 +43,41 @@ public class AcceptFlow implements ClientStartableFlow{ @Suspendable @Override public String call(ClientRequestBody requestBody) { - SecureHash gameStateUtxoTrxId = null; + SecureHash utxoTrxId = null; try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_ACCEPT); - final UUID gameStateUuid = UUID.fromString(requestBody.getRequestBody()); + final UUID gameUuid = UUID.fromString(requestBody.getRequestBody()); - final StateAndRef inputState = this.flowEngine - .subFlow(new GetFlow(gameStateUuid)); + final StateAndRef gameProposalSar = this.flowEngine + .subFlow(new GetFlow(gameUuid)); + final GameProposalState gameProposal = (GameProposalState)gameProposalSar.getState().getContractState(); - final GameBoardState outputState = new GameBoardState((GameProposalState)inputState.getState().getContractState()); + final GameBoardState gameBoard = new GameBoardState(gameProposal); // <<-- accepted - final UtxoSignedTransaction gameStateRejectTrx = utxoLedgerService.createTransactionBuilder() + final UtxoSignedTransaction gameProposalAcceptTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) - .addInputState(inputState.getRef()) - .addOutputState(outputState) - .addSignatories(inputState.getState().getContractState().getParticipants()) - .setNotary(inputState.getState().getNotaryName()) + .addInputState(gameProposalSar.getRef()) + .addOutputState(gameBoard) + .addSignatories(gameProposal.getParticipants()) + .setNotary(gameProposalSar.getState().getNotaryName()) .setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .toSignedTransaction(); - gameStateUtxoTrxId = this.flowEngine - .subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState))); + utxoTrxId = this.flowEngine + .subFlow(new CommitSubFlow(gameProposalAcceptTrx, command.getCounterparty(gameProposal))); - final View gameStateView = this.flowEngine - .subFlow(new ViewBuilder(gameStateUtxoTrxId)); + final View gameView = this.flowEngine + .subFlow(new ViewBuilder(utxoTrxId)); - return new FlowResponce(gameStateView, gameStateUtxoTrxId) + return new FlowResponce(gameView, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); e.printStackTrace(System.out); - return new FlowResponce(e, gameStateUtxoTrxId) + return new FlowResponce(e, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java index 9829234..a2ba205 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java @@ -13,6 +13,7 @@ import djmil.cordacheckers.gamestate.FlowResponce; import djmil.cordacheckers.gamestate.GetFlow; import djmil.cordacheckers.gamestate.View; import djmil.cordacheckers.gamestate.ViewBuilder; +import djmil.cordacheckers.states.GameProposalState; import djmil.cordacheckers.states.GameState; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; @@ -41,36 +42,37 @@ public class CancelFlow implements ClientStartableFlow{ @Suspendable @Override public String call(ClientRequestBody requestBody) { - SecureHash gameStateUtxoTrxId = null; + SecureHash utxoTrxId = null; try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CANCEL); - final UUID gameStateUuid = UUID.fromString(requestBody.getRequestBody()); + final UUID gameUuid = UUID.fromString(requestBody.getRequestBody()); - final StateAndRef inputState = this.flowEngine - .subFlow(new GetFlow(gameStateUuid)); + final StateAndRef gameProposalSar = this.flowEngine + .subFlow(new GetFlow(gameUuid)); + final GameProposalState gameProposal = (GameProposalState)gameProposalSar.getState().getContractState(); - final UtxoSignedTransaction gameStateRejectTrx = utxoLedgerService.createTransactionBuilder() + final UtxoSignedTransaction gameProposalCancelTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) - .addInputState(inputState.getRef()) - .addSignatories(inputState.getState().getContractState().getParticipants()) - .setNotary(inputState.getState().getNotaryName()) + .addInputState(gameProposalSar.getRef()) + .addSignatories(gameProposal.getParticipants()) + .setNotary(gameProposalSar.getState().getNotaryName()) .setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .toSignedTransaction(); - gameStateUtxoTrxId = this.flowEngine - .subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState))); + utxoTrxId = this.flowEngine + .subFlow(new CommitSubFlow(gameProposalCancelTrx, command.getCounterparty(gameProposal))); final View gameStateView = this.flowEngine - .subFlow(new ViewBuilder(gameStateUtxoTrxId)); + .subFlow(new ViewBuilder(utxoTrxId)); - return new FlowResponce(gameStateView, gameStateUtxoTrxId) + return new FlowResponce(gameStateView, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); - return new FlowResponce(e, gameStateUtxoTrxId) + return new FlowResponce(e, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java index 8e377c5..39382ed 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java @@ -53,60 +53,63 @@ public class CreateFlow implements ClientStartableFlow{ @Suspendable @Override public String call(ClientRequestBody requestBody) { - SecureHash gameStateUtxoTrxId = null; + SecureHash utxoTrxId = null; try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CREATE); - final GameProposalState newGameProposal = buildGameProposalStateFrom(requestBody); + final GameProposalState gameProposal = buildGameProposalStateFrom(requestBody); - final UtxoSignedTransaction utxoCandidate = utxoLedgerService.createTransactionBuilder() + final UtxoSignedTransaction gameProposalCreateTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) - .addOutputState(newGameProposal) - .addSignatories(newGameProposal.getParticipants()) + .addOutputState(gameProposal) + .addSignatories(gameProposal.getParticipants()) .setNotary(notaryLookup.getNotaryServices().iterator().next().getName()) .setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .toSignedTransaction(); - gameStateUtxoTrxId = this.flowEngine - .subFlow(new CommitSubFlow(utxoCandidate, command.getObserver(newGameProposal))); + utxoTrxId = this.flowEngine + .subFlow(new CommitSubFlow(gameProposalCreateTrx, command.getCounterparty(gameProposal))); - final View gameStateView = this.flowEngine - .subFlow(new ViewBuilder(gameStateUtxoTrxId)); + final View gameView = this.flowEngine + .subFlow(new ViewBuilder(utxoTrxId)); - return new FlowResponce(gameStateView, gameStateUtxoTrxId) + return new FlowResponce(gameView, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); - return new FlowResponce(e, gameStateUtxoTrxId) + return new FlowResponce(e, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } } @Suspendable private GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody) { - CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class); + final CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class); - Piece.Color opponentColor = Piece.Color.valueOf(args.opponentColor); + final Piece.Color opponentColor = Piece.Color.valueOf(args.opponentColor); if (opponentColor == null) { throw new RuntimeException("Allowed values for opponentColor are: " + Piece.Color.WHITE.name() +", " + Piece.Color.BLACK.name() + ". Actual value was: " + args.opponentColor); } - MemberInfo myInfo = memberLookup.myInfo(); - MemberInfo opponentInfo = requireNonNull( + final MemberInfo myInfo = memberLookup.myInfo(); + final MemberInfo opponentInfo = requireNonNull( memberLookup.lookup(MemberX500Name.parse(args.opponentName)), "MemberLookup can't find opponentName specified in flow arguments: " + args.opponentName ); + final MemberInfo whitePlayerInfo = opponentColor == Piece.Color.WHITE ? opponentInfo : myInfo; + final MemberInfo blackPlayerInfo = opponentColor == Piece.Color.BLACK ? opponentInfo : myInfo; + return new GameProposalState( - myInfo.getName(), - opponentInfo.getName(), - opponentColor, - args.message, + whitePlayerInfo.getName(), + blackPlayerInfo.getName(), + myInfo.getName(), // <<--- GameProposal issuer UUID.randomUUID(), + args.message, Arrays.asList(myInfo.getLedgerKeys().get(0), opponentInfo.getLedgerKeys().get(0)) ); } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java index a45310f..009f97a 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java @@ -13,6 +13,7 @@ import djmil.cordacheckers.gamestate.FlowResponce; import djmil.cordacheckers.gamestate.GetFlow; import djmil.cordacheckers.gamestate.View; import djmil.cordacheckers.gamestate.ViewBuilder; +import djmil.cordacheckers.states.GameProposalState; import djmil.cordacheckers.states.GameState; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; @@ -49,36 +50,37 @@ public class RejectFlow implements ClientStartableFlow{ @Suspendable @Override public String call(ClientRequestBody requestBody) { - SecureHash gameStateUtxoTrxId = null; + SecureHash utxoTrxId = null; try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_REJECT); - final UUID gameStateUuid = UUID.fromString(requestBody.getRequestBody()); + final UUID gameUuid = UUID.fromString(requestBody.getRequestBody()); - final StateAndRef inputState = this.flowEngine - .subFlow(new GetFlow(gameStateUuid)); + final StateAndRef gameProposalSar = this.flowEngine + .subFlow(new GetFlow(gameUuid)); + final GameProposalState gameProposal = (GameProposalState)gameProposalSar.getState().getContractState(); - final UtxoSignedTransaction gameStateRejectTrx = utxoLedgerService.createTransactionBuilder() + final UtxoSignedTransaction gameProposalRejectTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) - .addInputState(inputState.getRef()) - .addSignatories(inputState.getState().getContractState().getParticipants()) - .setNotary(inputState.getState().getNotaryName()) + .addInputState(gameProposalSar.getRef()) + .addSignatories(gameProposal.getParticipants()) + .setNotary(gameProposalSar.getState().getNotaryName()) .setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .toSignedTransaction(); - gameStateUtxoTrxId = this.flowEngine - .subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState))); + utxoTrxId = this.flowEngine + .subFlow(new CommitSubFlow(gameProposalRejectTrx, command.getCounterparty(gameProposal))); final View gameStateView = this.flowEngine - .subFlow(new ViewBuilder(gameStateUtxoTrxId)); + .subFlow(new ViewBuilder(utxoTrxId)); - return new FlowResponce(gameStateView, gameStateUtxoTrxId) + return new FlowResponce(gameStateView, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); - return new FlowResponce(e, gameStateUtxoTrxId) + return new FlowResponce(e, utxoTrxId) .toJsonEncodedString(jsonMarshallingService); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitSubFlowResponder.java b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitSubFlowResponder.java index c766145..0e81a57 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitSubFlowResponder.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitSubFlowResponder.java @@ -41,7 +41,7 @@ public class CommitSubFlowResponder implements ResponderFlow { UtxoTransactionValidator txValidator = trxToValidate -> { try { final GameCommand gameCommand = getSingleCommand(trxToValidate, GameCommand.class); - final GameState gameState = getGameStateFromTransaction(trxToValidate, gameCommand); + final GameState gameState = getActualGameStateFromTransaction(trxToValidate, gameCommand); checkParticipants(session, gameCommand, gameState); @@ -72,10 +72,10 @@ public class CommitSubFlowResponder implements ResponderFlow { * @param command * @return the most recent (from perspective of session participants validation) GameState for a given transaction * - * @see djmil.cordacheckers.gamestate.ViewBuilder#getGameStateFromTransaction(UtxoLedgerTransaction, GameCommand) + * @see djmil.cordacheckers.gamestate.ViewBuilder#getLatestGameStateFromTransaction(UtxoLedgerTransaction, GameCommand) */ @Suspendable - GameState getGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) { + GameState getActualGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) { switch (command.action) { case GAME_PROPOSAL_CREATE: return getSingleOutputState(gameStateTransaction, GameProposalState.class); @@ -85,10 +85,12 @@ public class CommitSubFlowResponder implements ResponderFlow { case GAME_PROPOSAL_CANCEL: return getSingleInputState(gameStateTransaction, GameProposalState.class); + case GAME_BOARD_SURRENDER: + return getSingleInputState(gameStateTransaction, GameBoardState.class); + case GAME_BOARD_MOVE: return getSingleOutputState(gameStateTransaction, GameBoardState.class); - case GAME_BOARD_SURRENDER: case GAME_RESULT_CREATE: return getSingleOutputState(gameStateTransaction, GameResultState.class); } @@ -97,22 +99,17 @@ public class CommitSubFlowResponder implements ResponderFlow { } @Suspendable - void checkParticipants(FlowSession session, GameCommand gameCommand, GameState outputGameState) throws ParticipantException { + void checkParticipants(FlowSession session, GameCommand gameCommand, GameState gameState) throws ParticipantException { final var myName = memberLookup.myInfo().getName(); - final var outputStateConterparty = outputGameState.getCounterpartyName(myName); - final var sessionConterparty = session.getCounterparty(); - - final var actor = gameCommand.getActor(outputGameState); - final var observer = gameCommand.getObserver(outputGameState); + final var opponentName = gameState.getOpponentName(myName); // throws NotInvolved + final var conterpartyName = session.getCounterparty(); + final var actorName = gameCommand.getInitiator(gameState); - if (outputStateConterparty.compareTo(sessionConterparty) != 0) - throw new ParticipantException("Counterparty", sessionConterparty, outputStateConterparty); + if (conterpartyName.compareTo(opponentName) != 0) + throw new ParticipantException("Counterparty", conterpartyName, opponentName); - if (actor.compareTo(sessionConterparty) != 0) - throw new ParticipantException("Actor", sessionConterparty, actor); - - if (observer.compareTo(myName) != 0) - throw new ParticipantException("Observer", myName, observer); + if (actorName.compareTo(conterpartyName) != 0) + throw new ParticipantException("Actor", conterpartyName, actorName); } public static class ParticipantException extends Exception { diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/View.java b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/View.java index 6a21166..4e0f126 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/View.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/View.java @@ -10,7 +10,6 @@ import djmil.cordacheckers.states.GameResultState; import djmil.cordacheckers.states.Piece; import net.corda.v5.base.types.MemberX500Name; - // GameBoard from the player's point of view public class View { public static enum Status { @@ -51,8 +50,8 @@ public class View { public View(View.Status status, GameProposalState gameProposal, MemberX500Name myName) { this.status = status; - this.opponentName = gameProposal.getCounterpartyName(myName).getCommonName(); - this.opponentColor = gameProposal.getCounterpartyColor(myName); + this.opponentName = gameProposal.getOpponentName(myName).getCommonName(); + this.opponentColor = gameProposal.getOpponentColor(myName); this.board = null; this.moveNumber = null; this.previousMove = null; @@ -62,8 +61,8 @@ public class View { public View(View.Status status, GameBoardState gameBoard, MemberX500Name myName) { this.status = status; - this.opponentName = gameBoard.getCounterpartyName(myName).getCommonName(); - this.opponentColor = gameBoard.getCounterpartyColor(myName); + this.opponentName = gameBoard.getOpponentName(myName).getCommonName(); + this.opponentColor = gameBoard.getOpponentColor(myName); this.board = gameBoard.getBoard(); this.moveNumber = gameBoard.getMoveNumber(); this.previousMove = null; @@ -73,8 +72,8 @@ public class View { public View(View.Status status, GameBoardState gameBoard, List previousMove, MemberX500Name myName) { this.status = status; - this.opponentName = gameBoard.getCounterpartyName(myName).getCommonName(); - this.opponentColor = gameBoard.getCounterpartyColor(myName); + this.opponentName = gameBoard.getOpponentName(myName).getCommonName(); + this.opponentColor = gameBoard.getOpponentColor(myName); this.board = gameBoard.getBoard(); this.moveNumber = gameBoard.getMoveNumber(); this.previousMove = previousMove; @@ -84,8 +83,8 @@ public class View { public View(View.Status status, GameResultState gameResult, MemberX500Name myName) { this.status = status; - this.opponentName = gameResult.getCounterpartyName(myName).getCommonName(); - this.opponentColor = gameResult.getCounterpartyColor(myName); + this.opponentName = gameResult.getOpponentName(myName).getCommonName(); + this.opponentColor = gameResult.getOpponentColor(myName); this.board = null; this.moveNumber = null; this.previousMove = null; diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/ViewBuilder.java b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/ViewBuilder.java index d899136..989fb62 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/ViewBuilder.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/ViewBuilder.java @@ -48,7 +48,7 @@ public class ViewBuilder implements SubFlow { MemberX500Name myName = memberLookup.myInfo().getName(); final GameCommand command = getSingleCommand(gameStateUtxo, GameCommand.class); - final GameState state = getGameStateFromTransaction(gameStateUtxo, command); + final GameState state = getLatestGameStateFromTransaction(gameStateUtxo, command); final View.Status viewStatus = action2status(command, state, myName); switch (command.action) { @@ -81,10 +81,10 @@ public class ViewBuilder implements SubFlow { * @param command * @return the most recent (from perspective of building a GameView) GameState for a given transaction * - * @see djmil.cordacheckers.gamestate.CommitSubFlowResponder#getGameStateFromTransaction(UtxoLedgerTransaction, GameCommand) + * @see djmil.cordacheckers.gamestate.CommitSubFlowResponder#getActualGameStateFromTransaction(UtxoLedgerTransaction, GameCommand) */ @Suspendable - GameState getGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) { + GameState getLatestGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) { switch (command.action) { case GAME_PROPOSAL_CREATE: return getSingleOutputState(gameStateTransaction, GameProposalState.class); @@ -107,7 +107,7 @@ public class ViewBuilder implements SubFlow { @Suspendable View.Status action2status(GameCommand command, GameState state, MemberX500Name myName) { - final boolean myAction = command.getActor(state).compareTo(myName) == 0; + final boolean myAction = command.getInitiator(state).compareTo(myName) == 0; switch (command.action) { case GAME_PROPOSAL_CREATE: