GameStates refactoring
This commit is contained in:
parent
959ea0051d
commit
9004e8a408
@ -21,15 +21,15 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
case GAME_PROPOSAL_ACCEPT:
|
case GAME_PROPOSAL_ACCEPT:
|
||||||
GameCommand.validateGameProposalAccept(trx);
|
command.validateGameProposalAccept(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_BOARD_MOVE:
|
case GAME_BOARD_MOVE:
|
||||||
GameCommand.validateGameBoardMove(trx);
|
command.validateGameBoardMove(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_BOARD_SURRENDER:
|
case GAME_BOARD_SURRENDER:
|
||||||
GameCommand.validateGameBoardSurrender(trx);
|
command.validateGameBoardSurrender(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package djmil.cordacheckers.contracts;
|
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.GameBoardState;
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import djmil.cordacheckers.states.GameResultState;
|
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.StateAndRef;
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
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 {
|
public class GameCommand implements Command {
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
public static enum Action {
|
public static enum Action {
|
||||||
@ -78,48 +77,49 @@ public class GameCommand implements Command {
|
|||||||
/*
|
/*
|
||||||
* Session initiator/respondent
|
* Session initiator/respondent
|
||||||
*/
|
*/
|
||||||
|
public MemberX500Name getCounterparty(StateAndRef<GameState> gameStateSar) {
|
||||||
public MemberX500Name getObserver(StateAndRef<GameState> gameStateSar) {
|
|
||||||
final GameState gameState = gameStateSar.getState().getContractState();
|
final GameState gameState = gameStateSar.getState().getContractState();
|
||||||
return gameState.getCounterpartyName(getActor(gameState));
|
return gameState.getOpponentName(getInitiator(gameState));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getActor(StateAndRef<GameState> gameStateSar) {
|
public MemberX500Name getCounterparty(GameState gameState) {
|
||||||
return getActor(gameStateSar.getState().getContractState());
|
return gameState.getOpponentName(getInitiator(gameState));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getObserver(GameState gameState) {
|
public MemberX500Name getInitiator(StateAndRef<GameState> gameStateSar) {
|
||||||
return gameState.getCounterpartyName(getActor(gameState));
|
return getInitiator(gameStateSar.getState().getContractState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getActor(GameState gameState) {
|
public MemberX500Name getInitiator(GameState gameState) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case GAME_PROPOSAL_CREATE:
|
case GAME_PROPOSAL_CREATE:
|
||||||
case GAME_PROPOSAL_CANCEL:
|
case GAME_PROPOSAL_CANCEL:
|
||||||
if (gameState instanceof GameProposalState)
|
if (gameState instanceof GameProposalState)
|
||||||
return ((GameProposalState)gameState).getIssuer();
|
return ((GameProposalState)gameState).getIssuerName();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_PROPOSAL_REJECT:
|
case GAME_PROPOSAL_REJECT:
|
||||||
if (gameState instanceof GameProposalState)
|
if (gameState instanceof GameProposalState)
|
||||||
return ((GameProposalState)gameState).getAcquier();
|
return ((GameProposalState)gameState).getAcquierName();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_PROPOSAL_ACCEPT:
|
case GAME_PROPOSAL_ACCEPT:
|
||||||
if (gameState instanceof GameProposalState) // <<-- Session validation perspective
|
if (gameState instanceof GameProposalState) // <<-- Session validation perspective
|
||||||
return ((GameProposalState)gameState).getAcquier();
|
return ((GameProposalState)gameState).getAcquierName();
|
||||||
if (gameState instanceof GameBoardState) // <<-- GameViewBuilder perspective
|
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;
|
break;
|
||||||
|
|
||||||
case GAME_BOARD_MOVE:
|
case GAME_BOARD_MOVE:
|
||||||
if (gameState instanceof GameBoardState)
|
if (gameState instanceof GameBoardState)
|
||||||
return ((GameBoardState)gameState).getMovePlayerName();
|
return ((GameBoardState)gameState).getActivePlayerName();
|
||||||
break;
|
|
||||||
|
|
||||||
case GAME_BOARD_SURRENDER:
|
|
||||||
if (gameState instanceof GameResultState)
|
|
||||||
return ((GameResultState)gameState).getOpponentName();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_RESULT_CREATE:
|
case GAME_RESULT_CREATE:
|
||||||
@ -138,75 +138,79 @@ public class GameCommand implements Command {
|
|||||||
* Transaction validation
|
* Transaction validation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static void validateGameProposalCreate(UtxoLedgerTransaction trx) {
|
public void validateGameProposalCreate(UtxoLedgerTransaction trx) {
|
||||||
requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE);
|
requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE);
|
||||||
requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_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.getInputContractStates().size() == 1, ACCEPT_INPUT_STATE);
|
||||||
requireThat(trx.getOutputContractStates().size() == 1, ACCEPT_OUTPUT_STATE);
|
requireThat(trx.getOutputContractStates().size() == 1, ACCEPT_OUTPUT_STATE);
|
||||||
|
|
||||||
GameProposalState inGameProposal = getSingleInputState(trx, GameProposalState.class);
|
final GameProposalState inGameProposal = getSingleInputState(trx, GameProposalState.class);
|
||||||
GameBoardState outGameBoard = getSingleOutputState(trx, GameBoardState.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.getInputContractStates().size() == 1, REJECT_INPUT_STATE);
|
||||||
requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE);
|
requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE);
|
||||||
|
|
||||||
getSingleInputState(trx, GameProposalState.class);
|
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.getInputContractStates().size() == 1, CANCEL_INPUT_STATE);
|
||||||
requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE);
|
requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE);
|
||||||
|
|
||||||
getSingleInputState(trx, GameProposalState.class);
|
getSingleInputState(trx, GameProposalState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateGameBoardSurrender(UtxoLedgerTransaction trx) {
|
public void validateGameBoardSurrender(UtxoLedgerTransaction trx) {
|
||||||
requireThat(trx.getInputContractStates().size() == 1, SURRENDER_INPUT_STATE);
|
requireThat(trx.getInputContractStates().size() == 1, SURRENDER_INPUT_STATE);
|
||||||
final var inGameBoardState = getSingleInputState(trx, GameBoardState.class);
|
final var inGameBoardState = getSingleInputState(trx, GameBoardState.class);
|
||||||
|
|
||||||
requireThat(trx.getOutputContractStates().size() == 1, SURRENDER_OUTPUT_STATE);
|
requireThat(trx.getOutputContractStates().size() == 1, SURRENDER_OUTPUT_STATE);
|
||||||
final var outGameResultState = getSingleOutputState(trx, GameResultState.class);
|
final var outGameResultState = getSingleOutputState(trx, GameResultState.class);
|
||||||
|
|
||||||
|
|
||||||
List<MemberX500Name> inActors = new LinkedList<MemberX500Name>(List.of(
|
List<MemberX500Name> inActors = new LinkedList<MemberX500Name>(List.of(
|
||||||
inGameBoardState.getWhitePlayerName(),
|
inGameBoardState.getWhitePlayer(),
|
||||||
inGameBoardState.getBlackPlayerName())
|
inGameBoardState.getBlackPlayer())
|
||||||
);
|
);
|
||||||
|
|
||||||
List<MemberX500Name> outActors = new LinkedList<MemberX500Name>(List.of(
|
List<MemberX500Name> outActors = new LinkedList<MemberX500Name>(List.of(
|
||||||
outGameResultState.getWinnerName(),
|
outGameResultState.getWhitePlayer(),
|
||||||
outGameResultState.getOpponentName())
|
outGameResultState.getBlackPlayer())
|
||||||
);
|
);
|
||||||
|
|
||||||
requireThat(inActors.containsAll(outActors) && outActors.containsAll(inActors), IN_OUT_PARTICIPANTS);
|
requireThat(inActors.containsAll(outActors) && outActors.containsAll(inActors), IN_OUT_PARTICIPANTS);
|
||||||
|
|
||||||
final var activePlayerName = outGameResultState.getCounterpartyName(outGameResultState.getWinnerName());
|
final var activePlayerName = getInitiator(inGameBoardState);
|
||||||
requireThat(inGameBoardState.getMovePlayerName().compareTo(activePlayerName) == 0, SURRENDER_ACTOR + "expected " +inGameBoardState.getMovePlayerName() +" actual " +activePlayerName);
|
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);
|
requireThat(trx.getInputContractStates().size() == 1, MOVE_INPUT_STATE);
|
||||||
final var inGameBoardState = getSingleInputState(trx, GameBoardState.class);
|
final var inGameBoardState = getSingleInputState(trx, GameBoardState.class);
|
||||||
|
|
||||||
requireThat(trx.getOutputContractStates().size() == 1, MOVE_OUTPUT_STATE);
|
requireThat(trx.getOutputContractStates().size() == 1, MOVE_OUTPUT_STATE);
|
||||||
final var outGameBoardState = getSingleOutputState(trx, GameBoardState.class);
|
final var outGameBoardState = getSingleOutputState(trx, GameBoardState.class);
|
||||||
|
|
||||||
requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameBoardState.getWhitePlayerName()) == 0, IN_OUT_PARTICIPANTS);
|
requireThat(inGameBoardState.getWhitePlayer().compareTo(outGameBoardState.getWhitePlayer()) == 0, IN_OUT_PARTICIPANTS);
|
||||||
requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameBoardState.getBlackPlayerName()) == 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");
|
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_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 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_INPUT_STATE = "Reject command should have exactly one GameProposal input state";
|
||||||
static final String REJECT_OUTPUT_STATE = "Reject command should have no output states";
|
static final String REJECT_OUTPUT_STATE = "Reject command should have no output states";
|
||||||
|
@ -21,19 +21,19 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
case GAME_PROPOSAL_CREATE:
|
case GAME_PROPOSAL_CREATE:
|
||||||
GameCommand.validateGameProposalCreate(trx);
|
command.validateGameProposalCreate(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_PROPOSAL_ACCEPT:
|
case GAME_PROPOSAL_ACCEPT:
|
||||||
GameCommand.validateGameProposalAccept(trx);
|
command.validateGameProposalAccept(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_PROPOSAL_REJECT:
|
case GAME_PROPOSAL_REJECT:
|
||||||
GameCommand.validateGameProposalReject(trx);
|
command.validateGameProposalReject(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_PROPOSAL_CANCEL:
|
case GAME_PROPOSAL_CANCEL:
|
||||||
GameCommand.validateGameProposalCancel(trx);
|
command.validateGameProposalCancel(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -21,11 +21,11 @@ public class GameResultContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
case GAME_BOARD_SURRENDER:
|
case GAME_BOARD_SURRENDER:
|
||||||
GameCommand.validateGameBoardSurrender(trx);
|
command.validateGameBoardSurrender(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GAME_RESULT_CREATE:
|
case GAME_RESULT_CREATE:
|
||||||
GameCommand.validateGameResultCreate(trx);
|
command.validateGameResultCreate(trx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -14,19 +14,13 @@ import net.corda.v5.ledger.utxo.BelongsToContract;
|
|||||||
|
|
||||||
@BelongsToContract(GameBoardContract.class)
|
@BelongsToContract(GameBoardContract.class)
|
||||||
public class GameBoardState extends GameState {
|
public class GameBoardState extends GameState {
|
||||||
|
|
||||||
private final MemberX500Name whitePlayerName;
|
|
||||||
private final MemberX500Name blackPlayerName;
|
|
||||||
|
|
||||||
private final Piece.Color moveColor;
|
private final Piece.Color moveColor;
|
||||||
private final Integer moveNumber;
|
private final Integer moveNumber;
|
||||||
private final Map<Integer, Piece> board;
|
private final Map<Integer, Piece> board;
|
||||||
|
|
||||||
public GameBoardState(GameProposalState gameProposalState) {
|
public GameBoardState(GameProposalState gameProposalState) {
|
||||||
super(gameProposalState.gameUuid, gameProposalState.message, gameProposalState.participants);
|
super(gameProposalState.whitePlayer, gameProposalState.blackPlayer,
|
||||||
|
gameProposalState.gameUuid, gameProposalState.message, gameProposalState.participants);
|
||||||
this.whitePlayerName = gameProposalState.getWhitePlayerName();
|
|
||||||
this.blackPlayerName = gameProposalState.getBlackPlayerName();
|
|
||||||
|
|
||||||
// Initial GameBoard state
|
// Initial GameBoard state
|
||||||
this.moveColor = Piece.Color.WHITE;
|
this.moveColor = Piece.Color.WHITE;
|
||||||
@ -34,43 +28,26 @@ public class GameBoardState extends GameState {
|
|||||||
this.board = new LinkedHashMap<Integer, Piece>(initialBoard);
|
this.board = new LinkedHashMap<Integer, Piece>(initialBoard);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameBoardState(
|
public GameBoardState(GameBoardState oldGameBoardState, Map<Integer, Piece> newBoard, Piece.Color moveColor) {
|
||||||
GameBoardState oldGameBoardState, Map<Integer, Piece> newBoard, Piece.Color moveColor) {
|
super(oldGameBoardState.whitePlayer, oldGameBoardState.blackPlayer,
|
||||||
super(oldGameBoardState.gameUuid, oldGameBoardState.message, oldGameBoardState.participants);
|
oldGameBoardState.gameUuid, oldGameBoardState.message, oldGameBoardState.participants);
|
||||||
|
|
||||||
this.whitePlayerName = oldGameBoardState.getWhitePlayerName();
|
|
||||||
this.blackPlayerName = oldGameBoardState.getBlackPlayerName();
|
|
||||||
|
|
||||||
// Initial GameBoard state
|
|
||||||
this.moveColor = moveColor;
|
this.moveColor = moveColor;
|
||||||
this.moveNumber = oldGameBoardState.getMoveNumber() +1;
|
this.moveNumber = oldGameBoardState.getMoveNumber() +1;
|
||||||
this.board = newBoard;
|
this.board = newBoard;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConstructorForDeserialization
|
@ConstructorForDeserialization
|
||||||
public GameBoardState(MemberX500Name whitePlayerName, MemberX500Name blackPlayerName,
|
public GameBoardState(MemberX500Name whitePlayer, MemberX500Name blackPlayer,
|
||||||
Color moveColor, Integer moveNumber, Map<Integer, Piece> board, String message,
|
Color moveColor, Integer moveNumber, Map<Integer, Piece> board, String message,
|
||||||
UUID gameUuid, List<PublicKey> participants) {
|
UUID gameUuid, List<PublicKey> participants) {
|
||||||
super(gameUuid, message, participants);
|
super(whitePlayer, blackPlayer, gameUuid, message, participants);
|
||||||
|
|
||||||
this.whitePlayerName = whitePlayerName;
|
|
||||||
this.blackPlayerName = blackPlayerName;
|
|
||||||
this.moveColor = moveColor;
|
this.moveColor = moveColor;
|
||||||
this.moveNumber = moveNumber;
|
this.moveNumber = moveNumber;
|
||||||
this.board = board;
|
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() {
|
public Piece.Color getMoveColor() {
|
||||||
return moveColor;
|
return moveColor;
|
||||||
}
|
}
|
||||||
@ -79,26 +56,14 @@ public class GameBoardState extends GameState {
|
|||||||
return moveNumber;
|
return moveNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getActivePlayerName() {
|
||||||
|
return moveColor == Piece.Color.WHITE ? whitePlayer : blackPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Integer, Piece> getBoard() {
|
public Map<Integer, Piece> getBoard() {
|
||||||
return board;
|
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
|
// TODO: move to contract
|
||||||
public final static Map<Integer, Piece> initialBoard = Map.ofEntries(
|
public final static Map<Integer, Piece> initialBoard = Map.ofEntries(
|
||||||
// Inspired by Checkers notation rules: https://www.bobnewell.net/nucleus/checkers.php
|
// Inspired by Checkers notation rules: https://www.bobnewell.net/nucleus/checkers.php
|
||||||
|
@ -12,50 +12,21 @@ import net.corda.v5.ledger.utxo.BelongsToContract;
|
|||||||
@BelongsToContract(GameProposalContract.class)
|
@BelongsToContract(GameProposalContract.class)
|
||||||
public class GameProposalState extends GameState {
|
public class GameProposalState extends GameState {
|
||||||
|
|
||||||
private final MemberX500Name issuer;
|
private final MemberX500Name issuerName;
|
||||||
private final MemberX500Name acquier;
|
|
||||||
private final Piece.Color acquierColor;
|
|
||||||
|
|
||||||
@ConstructorForDeserialization
|
@ConstructorForDeserialization
|
||||||
public GameProposalState(
|
public GameProposalState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, MemberX500Name issuerName,
|
||||||
MemberX500Name issuer,
|
UUID gameUuid, String message, List<PublicKey> participants) {
|
||||||
MemberX500Name acquier,
|
super(whitePlayer, blackPlayer, gameUuid, message, participants);
|
||||||
Piece.Color acquierColor,
|
this.issuerName = issuerName;
|
||||||
String message,
|
|
||||||
UUID gameUuid,
|
|
||||||
List<PublicKey> participants
|
|
||||||
) {
|
|
||||||
super(gameUuid, message, participants);
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.acquier = acquier;
|
|
||||||
this.acquierColor = acquierColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getIssuer() {
|
public MemberX500Name getIssuerName() {
|
||||||
return issuer;
|
return issuerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getAcquier() {
|
public MemberX500Name getAcquierName() {
|
||||||
return acquier;
|
return getOpponentName(issuerName);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,52 +13,25 @@ import net.corda.v5.ledger.utxo.BelongsToContract;
|
|||||||
public class GameResultState extends GameState {
|
public class GameResultState extends GameState {
|
||||||
|
|
||||||
private final MemberX500Name winnerName;
|
private final MemberX500Name winnerName;
|
||||||
private final MemberX500Name opponentName;
|
|
||||||
|
|
||||||
private final Piece.Color winnerColor;
|
|
||||||
|
|
||||||
@ConstructorForDeserialization
|
@ConstructorForDeserialization
|
||||||
public GameResultState(MemberX500Name winnerName, MemberX500Name opponentName, Piece.Color winnerColor,
|
public GameResultState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, MemberX500Name winnerName,
|
||||||
UUID gameUuid, String message, List<PublicKey> participants) {
|
UUID gameUuid, String message, List<PublicKey> participants) {
|
||||||
super(gameUuid, message, participants);
|
super(whitePlayer, blackPlayer, gameUuid, message, participants);
|
||||||
this.winnerName = winnerName;
|
this.winnerName = winnerName;
|
||||||
this.opponentName = opponentName;
|
|
||||||
this.winnerColor = winnerColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameResultState(GameBoardState gameBoardState, MemberX500Name winnerName) {
|
public GameResultState(GameBoardState gameBoardState, MemberX500Name winnerName) {
|
||||||
super(gameBoardState.gameUuid, null, gameBoardState.participants);
|
super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants);
|
||||||
this.opponentName = gameBoardState.getCounterpartyName(winnerName);
|
|
||||||
this.winnerName = winnerName;
|
this.winnerName = winnerName;
|
||||||
this.winnerColor = gameBoardState.getWhitePlayerName().compareTo(winnerName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getWinnerName() {
|
public MemberX500Name getWinnerName() {
|
||||||
return winnerName;
|
return winnerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getOpponentName() {
|
public MemberX500Name getLooserName() {
|
||||||
return opponentName;
|
return getOpponentName(getWinnerName());
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,30 +4,35 @@ import java.security.PublicKey;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import net.corda.v5.base.annotations.CordaSerializable;
|
import net.corda.v5.base.annotations.CordaSerializable;
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
import net.corda.v5.ledger.utxo.ContractState;
|
import net.corda.v5.ledger.utxo.ContractState;
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
public abstract class GameState implements ContractState {
|
public abstract class GameState implements ContractState {
|
||||||
|
final MemberX500Name whitePlayer;
|
||||||
|
final MemberX500Name blackPlayer;
|
||||||
final UUID gameUuid;
|
final UUID gameUuid;
|
||||||
final String message;
|
final String message;
|
||||||
final List<PublicKey> participants;
|
final List<PublicKey> participants;
|
||||||
|
|
||||||
@NotNull
|
GameState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, UUID gameUuid,
|
||||||
public abstract MemberX500Name getWhitePlayerName();
|
String message, List<PublicKey> participants) {
|
||||||
|
this.whitePlayer = whitePlayer;
|
||||||
@NotNull
|
this.blackPlayer = blackPlayer;
|
||||||
public abstract MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved;
|
|
||||||
|
|
||||||
GameState(UUID gameUuid, String message, List<PublicKey> participants) {
|
|
||||||
this.gameUuid = gameUuid;
|
this.gameUuid = gameUuid;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.participants = participants;
|
this.participants = participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getWhitePlayer() {
|
||||||
|
return whitePlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getBlackPlayer() {
|
||||||
|
return blackPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
public UUID getGameUuid() {
|
public UUID getGameUuid() {
|
||||||
return gameUuid;
|
return gameUuid;
|
||||||
}
|
}
|
||||||
@ -36,18 +41,25 @@ public abstract class GameState implements ContractState {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<PublicKey> getParticipants() {
|
public List<PublicKey> getParticipants() {
|
||||||
return participants;
|
return participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getBlackPlayerName() {
|
public MemberX500Name getOpponentName(MemberX500Name playerName) throws NotInvolved {
|
||||||
return getCounterpartyName(getWhitePlayerName());
|
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 {
|
public Piece.Color getOpponentColor(MemberX500Name playerName) throws NotInvolved {
|
||||||
final MemberX500Name opponentName = getCounterpartyName(myName);
|
if (playerName.compareTo(whitePlayer) == 0)
|
||||||
|
return Piece.Color.BLACK;
|
||||||
return getWhitePlayerName().compareTo(opponentName) == 0 ? Piece.Color.WHITE : 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 {
|
public static class NotInvolved extends RuntimeException {
|
||||||
|
@ -52,7 +52,7 @@ public class SurrenderFlow implements ClientStartableFlow{
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
SecureHash gameStateUtxoTrxId = null;
|
SecureHash utxoTrxId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GameCommand command = new GameCommand(GameCommand.Action.GAME_BOARD_SURRENDER);
|
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 GameResultState outputState = prepareGameResultState(inputStateSar);
|
||||||
|
|
||||||
final UtxoSignedTransaction transactionCandidate = utxoLedgerService.createTransactionBuilder()
|
final UtxoSignedTransaction gameBoardSurrenderTrx = utxoLedgerService.createTransactionBuilder()
|
||||||
.addCommand(command)
|
.addCommand(command)
|
||||||
.addInputState(inputStateSar.getRef())
|
.addInputState(inputStateSar.getRef())
|
||||||
.addOutputState(outputState)
|
.addOutputState(outputState)
|
||||||
@ -73,19 +73,19 @@ public class SurrenderFlow implements ClientStartableFlow{
|
|||||||
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.toSignedTransaction();
|
.toSignedTransaction();
|
||||||
|
|
||||||
gameStateUtxoTrxId = this.flowEngine
|
utxoTrxId = this.flowEngine
|
||||||
.subFlow(new CommitSubFlow(transactionCandidate, command.getObserver(outputState)));
|
.subFlow(new CommitSubFlow(gameBoardSurrenderTrx, command.getCounterparty(inputStateSar)));
|
||||||
|
|
||||||
final View gameStateView = this.flowEngine
|
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);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
||||||
e.printStackTrace(System.out);
|
e.printStackTrace(System.out);
|
||||||
return new FlowResponce(e, gameStateUtxoTrxId)
|
return new FlowResponce(e, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ public class SurrenderFlow implements ClientStartableFlow{
|
|||||||
final GameBoardState gameBoard = (GameBoardState) gameState;
|
final GameBoardState gameBoard = (GameBoardState) gameState;
|
||||||
|
|
||||||
final MemberX500Name myName = memberLookup.myInfo().getName();
|
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
final MemberX500Name winnerName = gameBoard.getCounterpartyName(myName);
|
final MemberX500Name winnerName = gameBoard.getOpponentName(myName);
|
||||||
|
|
||||||
return new GameResultState(gameBoard, winnerName);
|
return new GameResultState(gameBoard, winnerName);
|
||||||
}
|
}
|
||||||
|
@ -43,40 +43,41 @@ public class AcceptFlow implements ClientStartableFlow{
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
SecureHash gameStateUtxoTrxId = null;
|
SecureHash utxoTrxId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_ACCEPT);
|
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<GameState> inputState = this.flowEngine
|
final StateAndRef<GameState> gameProposalSar = this.flowEngine
|
||||||
.subFlow(new GetFlow(gameStateUuid));
|
.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)
|
.addCommand(command)
|
||||||
.addInputState(inputState.getRef())
|
.addInputState(gameProposalSar.getRef())
|
||||||
.addOutputState(outputState)
|
.addOutputState(gameBoard)
|
||||||
.addSignatories(inputState.getState().getContractState().getParticipants())
|
.addSignatories(gameProposal.getParticipants())
|
||||||
.setNotary(inputState.getState().getNotaryName())
|
.setNotary(gameProposalSar.getState().getNotaryName())
|
||||||
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.toSignedTransaction();
|
.toSignedTransaction();
|
||||||
|
|
||||||
gameStateUtxoTrxId = this.flowEngine
|
utxoTrxId = this.flowEngine
|
||||||
.subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState)));
|
.subFlow(new CommitSubFlow(gameProposalAcceptTrx, command.getCounterparty(gameProposal)));
|
||||||
|
|
||||||
final View gameStateView = this.flowEngine
|
final View gameView = this.flowEngine
|
||||||
.subFlow(new ViewBuilder(gameStateUtxoTrxId));
|
.subFlow(new ViewBuilder(utxoTrxId));
|
||||||
|
|
||||||
return new FlowResponce(gameStateView, gameStateUtxoTrxId)
|
return new FlowResponce(gameView, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
||||||
e.printStackTrace(System.out);
|
e.printStackTrace(System.out);
|
||||||
return new FlowResponce(e, gameStateUtxoTrxId)
|
return new FlowResponce(e, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import djmil.cordacheckers.gamestate.FlowResponce;
|
|||||||
import djmil.cordacheckers.gamestate.GetFlow;
|
import djmil.cordacheckers.gamestate.GetFlow;
|
||||||
import djmil.cordacheckers.gamestate.View;
|
import djmil.cordacheckers.gamestate.View;
|
||||||
import djmil.cordacheckers.gamestate.ViewBuilder;
|
import djmil.cordacheckers.gamestate.ViewBuilder;
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import djmil.cordacheckers.states.GameState;
|
import djmil.cordacheckers.states.GameState;
|
||||||
import net.corda.v5.application.flows.ClientRequestBody;
|
import net.corda.v5.application.flows.ClientRequestBody;
|
||||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||||
@ -41,36 +42,37 @@ public class CancelFlow implements ClientStartableFlow{
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
SecureHash gameStateUtxoTrxId = null;
|
SecureHash utxoTrxId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CANCEL);
|
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<GameState> inputState = this.flowEngine
|
final StateAndRef<GameState> gameProposalSar = this.flowEngine
|
||||||
.subFlow(new GetFlow(gameStateUuid));
|
.subFlow(new GetFlow(gameUuid));
|
||||||
|
final GameProposalState gameProposal = (GameProposalState)gameProposalSar.getState().getContractState();
|
||||||
|
|
||||||
final UtxoSignedTransaction gameStateRejectTrx = utxoLedgerService.createTransactionBuilder()
|
final UtxoSignedTransaction gameProposalCancelTrx = utxoLedgerService.createTransactionBuilder()
|
||||||
.addCommand(command)
|
.addCommand(command)
|
||||||
.addInputState(inputState.getRef())
|
.addInputState(gameProposalSar.getRef())
|
||||||
.addSignatories(inputState.getState().getContractState().getParticipants())
|
.addSignatories(gameProposal.getParticipants())
|
||||||
.setNotary(inputState.getState().getNotaryName())
|
.setNotary(gameProposalSar.getState().getNotaryName())
|
||||||
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.toSignedTransaction();
|
.toSignedTransaction();
|
||||||
|
|
||||||
gameStateUtxoTrxId = this.flowEngine
|
utxoTrxId = this.flowEngine
|
||||||
.subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState)));
|
.subFlow(new CommitSubFlow(gameProposalCancelTrx, command.getCounterparty(gameProposal)));
|
||||||
|
|
||||||
final View gameStateView = this.flowEngine
|
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);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
||||||
return new FlowResponce(e, gameStateUtxoTrxId)
|
return new FlowResponce(e, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,60 +53,63 @@ public class CreateFlow implements ClientStartableFlow{
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
SecureHash gameStateUtxoTrxId = null;
|
SecureHash utxoTrxId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CREATE);
|
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)
|
.addCommand(command)
|
||||||
.addOutputState(newGameProposal)
|
.addOutputState(gameProposal)
|
||||||
.addSignatories(newGameProposal.getParticipants())
|
.addSignatories(gameProposal.getParticipants())
|
||||||
.setNotary(notaryLookup.getNotaryServices().iterator().next().getName())
|
.setNotary(notaryLookup.getNotaryServices().iterator().next().getName())
|
||||||
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.toSignedTransaction();
|
.toSignedTransaction();
|
||||||
|
|
||||||
gameStateUtxoTrxId = this.flowEngine
|
utxoTrxId = this.flowEngine
|
||||||
.subFlow(new CommitSubFlow(utxoCandidate, command.getObserver(newGameProposal)));
|
.subFlow(new CommitSubFlow(gameProposalCreateTrx, command.getCounterparty(gameProposal)));
|
||||||
|
|
||||||
final View gameStateView = this.flowEngine
|
final View gameView = this.flowEngine
|
||||||
.subFlow(new ViewBuilder(gameStateUtxoTrxId));
|
.subFlow(new ViewBuilder(utxoTrxId));
|
||||||
|
|
||||||
return new FlowResponce(gameStateView, gameStateUtxoTrxId)
|
return new FlowResponce(gameView, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
||||||
return new FlowResponce(e, gameStateUtxoTrxId)
|
return new FlowResponce(e, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody) {
|
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) {
|
if (opponentColor == null) {
|
||||||
throw new RuntimeException("Allowed values for opponentColor are: "
|
throw new RuntimeException("Allowed values for opponentColor are: "
|
||||||
+ Piece.Color.WHITE.name() +", " + Piece.Color.BLACK.name()
|
+ Piece.Color.WHITE.name() +", " + Piece.Color.BLACK.name()
|
||||||
+ ". Actual value was: " + args.opponentColor);
|
+ ". Actual value was: " + args.opponentColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemberInfo myInfo = memberLookup.myInfo();
|
final MemberInfo myInfo = memberLookup.myInfo();
|
||||||
MemberInfo opponentInfo = requireNonNull(
|
final MemberInfo opponentInfo = requireNonNull(
|
||||||
memberLookup.lookup(MemberX500Name.parse(args.opponentName)),
|
memberLookup.lookup(MemberX500Name.parse(args.opponentName)),
|
||||||
"MemberLookup can't find opponentName specified in flow arguments: " + 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(
|
return new GameProposalState(
|
||||||
myInfo.getName(),
|
whitePlayerInfo.getName(),
|
||||||
opponentInfo.getName(),
|
blackPlayerInfo.getName(),
|
||||||
opponentColor,
|
myInfo.getName(), // <<--- GameProposal issuer
|
||||||
args.message,
|
|
||||||
UUID.randomUUID(),
|
UUID.randomUUID(),
|
||||||
|
args.message,
|
||||||
Arrays.asList(myInfo.getLedgerKeys().get(0), opponentInfo.getLedgerKeys().get(0))
|
Arrays.asList(myInfo.getLedgerKeys().get(0), opponentInfo.getLedgerKeys().get(0))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import djmil.cordacheckers.gamestate.FlowResponce;
|
|||||||
import djmil.cordacheckers.gamestate.GetFlow;
|
import djmil.cordacheckers.gamestate.GetFlow;
|
||||||
import djmil.cordacheckers.gamestate.View;
|
import djmil.cordacheckers.gamestate.View;
|
||||||
import djmil.cordacheckers.gamestate.ViewBuilder;
|
import djmil.cordacheckers.gamestate.ViewBuilder;
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import djmil.cordacheckers.states.GameState;
|
import djmil.cordacheckers.states.GameState;
|
||||||
import net.corda.v5.application.flows.ClientRequestBody;
|
import net.corda.v5.application.flows.ClientRequestBody;
|
||||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||||
@ -49,36 +50,37 @@ public class RejectFlow implements ClientStartableFlow{
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
SecureHash gameStateUtxoTrxId = null;
|
SecureHash utxoTrxId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_REJECT);
|
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<GameState> inputState = this.flowEngine
|
final StateAndRef<GameState> gameProposalSar = this.flowEngine
|
||||||
.subFlow(new GetFlow(gameStateUuid));
|
.subFlow(new GetFlow(gameUuid));
|
||||||
|
final GameProposalState gameProposal = (GameProposalState)gameProposalSar.getState().getContractState();
|
||||||
|
|
||||||
final UtxoSignedTransaction gameStateRejectTrx = utxoLedgerService.createTransactionBuilder()
|
final UtxoSignedTransaction gameProposalRejectTrx = utxoLedgerService.createTransactionBuilder()
|
||||||
.addCommand(command)
|
.addCommand(command)
|
||||||
.addInputState(inputState.getRef())
|
.addInputState(gameProposalSar.getRef())
|
||||||
.addSignatories(inputState.getState().getContractState().getParticipants())
|
.addSignatories(gameProposal.getParticipants())
|
||||||
.setNotary(inputState.getState().getNotaryName())
|
.setNotary(gameProposalSar.getState().getNotaryName())
|
||||||
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowUntil(Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.toSignedTransaction();
|
.toSignedTransaction();
|
||||||
|
|
||||||
gameStateUtxoTrxId = this.flowEngine
|
utxoTrxId = this.flowEngine
|
||||||
.subFlow(new CommitSubFlow(gameStateRejectTrx, command.getObserver(inputState)));
|
.subFlow(new CommitSubFlow(gameProposalRejectTrx, command.getCounterparty(gameProposal)));
|
||||||
|
|
||||||
final View gameStateView = this.flowEngine
|
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);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
log.warn("Exception during processing " + requestBody + " request: " + e.getMessage());
|
||||||
return new FlowResponce(e, gameStateUtxoTrxId)
|
return new FlowResponce(e, utxoTrxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public class CommitSubFlowResponder implements ResponderFlow {
|
|||||||
UtxoTransactionValidator txValidator = trxToValidate -> {
|
UtxoTransactionValidator txValidator = trxToValidate -> {
|
||||||
try {
|
try {
|
||||||
final GameCommand gameCommand = getSingleCommand(trxToValidate, GameCommand.class);
|
final GameCommand gameCommand = getSingleCommand(trxToValidate, GameCommand.class);
|
||||||
final GameState gameState = getGameStateFromTransaction(trxToValidate, gameCommand);
|
final GameState gameState = getActualGameStateFromTransaction(trxToValidate, gameCommand);
|
||||||
|
|
||||||
checkParticipants(session, gameCommand, gameState);
|
checkParticipants(session, gameCommand, gameState);
|
||||||
|
|
||||||
@ -72,10 +72,10 @@ public class CommitSubFlowResponder implements ResponderFlow {
|
|||||||
* @param command
|
* @param command
|
||||||
* @return the most recent (from perspective of session participants validation) GameState for a given transaction
|
* @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
|
@Suspendable
|
||||||
GameState getGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) {
|
GameState getActualGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) {
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
case GAME_PROPOSAL_CREATE:
|
case GAME_PROPOSAL_CREATE:
|
||||||
return getSingleOutputState(gameStateTransaction, GameProposalState.class);
|
return getSingleOutputState(gameStateTransaction, GameProposalState.class);
|
||||||
@ -85,10 +85,12 @@ public class CommitSubFlowResponder implements ResponderFlow {
|
|||||||
case GAME_PROPOSAL_CANCEL:
|
case GAME_PROPOSAL_CANCEL:
|
||||||
return getSingleInputState(gameStateTransaction, GameProposalState.class);
|
return getSingleInputState(gameStateTransaction, GameProposalState.class);
|
||||||
|
|
||||||
|
case GAME_BOARD_SURRENDER:
|
||||||
|
return getSingleInputState(gameStateTransaction, GameBoardState.class);
|
||||||
|
|
||||||
case GAME_BOARD_MOVE:
|
case GAME_BOARD_MOVE:
|
||||||
return getSingleOutputState(gameStateTransaction, GameBoardState.class);
|
return getSingleOutputState(gameStateTransaction, GameBoardState.class);
|
||||||
|
|
||||||
case GAME_BOARD_SURRENDER:
|
|
||||||
case GAME_RESULT_CREATE:
|
case GAME_RESULT_CREATE:
|
||||||
return getSingleOutputState(gameStateTransaction, GameResultState.class);
|
return getSingleOutputState(gameStateTransaction, GameResultState.class);
|
||||||
}
|
}
|
||||||
@ -97,22 +99,17 @@ public class CommitSubFlowResponder implements ResponderFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@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 myName = memberLookup.myInfo().getName();
|
||||||
final var outputStateConterparty = outputGameState.getCounterpartyName(myName);
|
final var opponentName = gameState.getOpponentName(myName); // throws NotInvolved
|
||||||
final var sessionConterparty = session.getCounterparty();
|
final var conterpartyName = session.getCounterparty();
|
||||||
|
final var actorName = gameCommand.getInitiator(gameState);
|
||||||
final var actor = gameCommand.getActor(outputGameState);
|
|
||||||
final var observer = gameCommand.getObserver(outputGameState);
|
|
||||||
|
|
||||||
if (outputStateConterparty.compareTo(sessionConterparty) != 0)
|
if (conterpartyName.compareTo(opponentName) != 0)
|
||||||
throw new ParticipantException("Counterparty", sessionConterparty, outputStateConterparty);
|
throw new ParticipantException("Counterparty", conterpartyName, opponentName);
|
||||||
|
|
||||||
if (actor.compareTo(sessionConterparty) != 0)
|
if (actorName.compareTo(conterpartyName) != 0)
|
||||||
throw new ParticipantException("Actor", sessionConterparty, actor);
|
throw new ParticipantException("Actor", conterpartyName, actorName);
|
||||||
|
|
||||||
if (observer.compareTo(myName) != 0)
|
|
||||||
throw new ParticipantException("Observer", myName, observer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ParticipantException extends Exception {
|
public static class ParticipantException extends Exception {
|
||||||
|
@ -10,7 +10,6 @@ import djmil.cordacheckers.states.GameResultState;
|
|||||||
import djmil.cordacheckers.states.Piece;
|
import djmil.cordacheckers.states.Piece;
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
|
|
||||||
|
|
||||||
// GameBoard from the player's point of view
|
// GameBoard from the player's point of view
|
||||||
public class View {
|
public class View {
|
||||||
public static enum Status {
|
public static enum Status {
|
||||||
@ -51,8 +50,8 @@ public class View {
|
|||||||
|
|
||||||
public View(View.Status status, GameProposalState gameProposal, MemberX500Name myName) {
|
public View(View.Status status, GameProposalState gameProposal, MemberX500Name myName) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.opponentName = gameProposal.getCounterpartyName(myName).getCommonName();
|
this.opponentName = gameProposal.getOpponentName(myName).getCommonName();
|
||||||
this.opponentColor = gameProposal.getCounterpartyColor(myName);
|
this.opponentColor = gameProposal.getOpponentColor(myName);
|
||||||
this.board = null;
|
this.board = null;
|
||||||
this.moveNumber = null;
|
this.moveNumber = null;
|
||||||
this.previousMove = null;
|
this.previousMove = null;
|
||||||
@ -62,8 +61,8 @@ public class View {
|
|||||||
|
|
||||||
public View(View.Status status, GameBoardState gameBoard, MemberX500Name myName) {
|
public View(View.Status status, GameBoardState gameBoard, MemberX500Name myName) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.opponentName = gameBoard.getCounterpartyName(myName).getCommonName();
|
this.opponentName = gameBoard.getOpponentName(myName).getCommonName();
|
||||||
this.opponentColor = gameBoard.getCounterpartyColor(myName);
|
this.opponentColor = gameBoard.getOpponentColor(myName);
|
||||||
this.board = gameBoard.getBoard();
|
this.board = gameBoard.getBoard();
|
||||||
this.moveNumber = gameBoard.getMoveNumber();
|
this.moveNumber = gameBoard.getMoveNumber();
|
||||||
this.previousMove = null;
|
this.previousMove = null;
|
||||||
@ -73,8 +72,8 @@ public class View {
|
|||||||
|
|
||||||
public View(View.Status status, GameBoardState gameBoard, List<Integer> previousMove, MemberX500Name myName) {
|
public View(View.Status status, GameBoardState gameBoard, List<Integer> previousMove, MemberX500Name myName) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.opponentName = gameBoard.getCounterpartyName(myName).getCommonName();
|
this.opponentName = gameBoard.getOpponentName(myName).getCommonName();
|
||||||
this.opponentColor = gameBoard.getCounterpartyColor(myName);
|
this.opponentColor = gameBoard.getOpponentColor(myName);
|
||||||
this.board = gameBoard.getBoard();
|
this.board = gameBoard.getBoard();
|
||||||
this.moveNumber = gameBoard.getMoveNumber();
|
this.moveNumber = gameBoard.getMoveNumber();
|
||||||
this.previousMove = previousMove;
|
this.previousMove = previousMove;
|
||||||
@ -84,8 +83,8 @@ public class View {
|
|||||||
|
|
||||||
public View(View.Status status, GameResultState gameResult, MemberX500Name myName) {
|
public View(View.Status status, GameResultState gameResult, MemberX500Name myName) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.opponentName = gameResult.getCounterpartyName(myName).getCommonName();
|
this.opponentName = gameResult.getOpponentName(myName).getCommonName();
|
||||||
this.opponentColor = gameResult.getCounterpartyColor(myName);
|
this.opponentColor = gameResult.getOpponentColor(myName);
|
||||||
this.board = null;
|
this.board = null;
|
||||||
this.moveNumber = null;
|
this.moveNumber = null;
|
||||||
this.previousMove = null;
|
this.previousMove = null;
|
||||||
|
@ -48,7 +48,7 @@ public class ViewBuilder implements SubFlow<View> {
|
|||||||
MemberX500Name myName = memberLookup.myInfo().getName();
|
MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
final GameCommand command = getSingleCommand(gameStateUtxo, GameCommand.class);
|
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);
|
final View.Status viewStatus = action2status(command, state, myName);
|
||||||
|
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
@ -81,10 +81,10 @@ public class ViewBuilder implements SubFlow<View> {
|
|||||||
* @param command
|
* @param command
|
||||||
* @return the most recent (from perspective of building a GameView) GameState for a given transaction
|
* @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
|
@Suspendable
|
||||||
GameState getGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) {
|
GameState getLatestGameStateFromTransaction(UtxoLedgerTransaction gameStateTransaction, GameCommand command) {
|
||||||
switch (command.action) {
|
switch (command.action) {
|
||||||
case GAME_PROPOSAL_CREATE:
|
case GAME_PROPOSAL_CREATE:
|
||||||
return getSingleOutputState(gameStateTransaction, GameProposalState.class);
|
return getSingleOutputState(gameStateTransaction, GameProposalState.class);
|
||||||
@ -107,7 +107,7 @@ public class ViewBuilder implements SubFlow<View> {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
View.Status action2status(GameCommand command, GameState state, MemberX500Name myName) {
|
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) {
|
switch (command.action) {
|
||||||
case GAME_PROPOSAL_CREATE:
|
case GAME_PROPOSAL_CREATE:
|
||||||
|
Loading…
Reference in New Issue
Block a user