diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java index d28b8af..a19dde8 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java @@ -13,9 +13,9 @@ public class GameBoardCommand implements Command { public static enum Type { MOVE, SURRENDER, - REQUEST_DRAW, - REQUEST_VICTORY, - FINISH; // aka accept DRAW or VICTORY request + DRAW, + VICTORY, + ACCEPT; // aka accept opponents DRAW or VICTORY request } private final Type type; 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 63fac64..4945bfb 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardContract.java @@ -22,23 +22,34 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract { public void verify(UtxoLedgerTransaction trx) { log.info("GameBoardContract.verify() called"); + requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND); final Command command = getSingleCommand(trx, Command.class); if (command instanceof GameProposalCommand) { log.info("GameBoardContract.verify() as GameProposalCommand "+(GameProposalCommand)command); + switch ((GameProposalCommand)command) { + case ACCEPT: + GameProposalCommand.validateAcceptTrx(trx); + break; + + default: + throw new CordaRuntimeException(UNKNOWN_COMMAND); + } } else if (command instanceof GameBoardCommand) { log.info("GameBoardContract.verify() as GameBoardCommand "+((GameBoardCommand)command).getType()); switch (((GameBoardCommand)command).getType()) { case MOVE: break; + case SURRENDER: break; - case REQUEST_DRAW: + + case DRAW: break; - case REQUEST_VICTORY: + case VICTORY: break; - case FINISH: + case ACCEPT: break; } } else { diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalCommand.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalCommand.java index 213ab12..54e0fca 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalCommand.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalCommand.java @@ -1,9 +1,15 @@ package djmil.cordacheckers.contracts; +import djmil.cordacheckers.states.GameBoardState; import djmil.cordacheckers.states.GameProposalState; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.Command; import net.corda.v5.ledger.utxo.StateAndRef; +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction; + +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleInputState; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleOutputState; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.requireThat; public enum GameProposalCommand implements Command { CREATE, @@ -45,5 +51,52 @@ public enum GameProposalCommand implements Command { return getRespondent(utxoGameProposal.getState().getContractState()); } + public static void validateCreateTrx(UtxoLedgerTransaction trx) { + requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE); + requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE); + + GameProposalState outputState = getSingleOutputState(trx, GameProposalState.class); + + requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR); + } + + public static void validateAcceptTrx(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); + + requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), ACCEPT_PARTICIPANTS); + } + + public static void validateRejectTrx(UtxoLedgerTransaction trx) { + requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE); + requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE); + + getSingleInputState(trx, GameProposalState.class); + } + + public static void validateCancelTrx(UtxoLedgerTransaction trx) { + requireThat(trx.getInputContractStates().size() == 1, CANCEL_INPUT_STATE); + requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE); + + getSingleInputState(trx, GameProposalState.class); + } + public static final String UNSUPPORTED_VALUE_OF = "Unsupported GameProposalCommand value: "; + + 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"; + + static final String CANCEL_INPUT_STATE = "Cancel command should have exactly one GameProposal input state"; + static final String CANCEL_OUTPUT_STATE = "Cancel command should have no output states"; + + static final String ACCEPT_INPUT_STATE = "Accept command should have exactly one GameProposal input state"; + static final String ACCEPT_OUTPUT_STATE = "Accept command should have exactly one GameBoard output state"; + static final String ACCEPT_PARTICIPANTS = "Accept command: GameBoard participants should math GameProposal participants"; } 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 01e3dbe..0044774 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameProposalContract.java @@ -1,17 +1,14 @@ package djmil.cordacheckers.contracts; -import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleCommand; -import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleInputState; -import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleOutputState; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import djmil.cordacheckers.states.GameBoardState; -import djmil.cordacheckers.states.GameProposalState; import net.corda.v5.base.exceptions.CordaRuntimeException; import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleCommand; +import static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.requireThat; + public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract { private final static Logger log = LoggerFactory.getLogger(GameProposalContract.class); @@ -20,67 +17,31 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract { public void verify(UtxoLedgerTransaction trx) { log.info("GameProposalContract.verify() called"); + requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND); final GameProposalCommand command = getSingleCommand(trx, GameProposalCommand.class); switch (command) { - case CREATE: { - requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE); - requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE); + case CREATE: + GameProposalCommand.validateCreateTrx(trx); + break; - GameProposalState outputState = getSingleOutputState(trx, GameProposalState.class); + case ACCEPT: + GameProposalCommand.validateAcceptTrx(trx); + break; - requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR); - break; } - - case ACCEPT: { - 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); - - requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), ACCEPT_PARTICIPANTS); - break; } - - case REJECT: { - requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE); - requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE); - - getSingleInputState(trx, GameProposalState.class); - break; } + case REJECT: + GameProposalCommand.validateRejectTrx(trx); + break; case CANCEL: - requireThat(trx.getInputContractStates().size() == 1, CANCEL_INPUT_STATE); - requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE); - - getSingleInputState(trx, GameProposalState.class); + GameProposalCommand.validateCancelTrx(trx); break; default: throw new CordaRuntimeException(UNKNOWN_COMMAND); } } - - private void requireThat(boolean asserted, String errorMessage) { - if (!asserted) { - throw new CordaRuntimeException("Failed requirement: " + errorMessage); - } - } static final String REQUIRE_SINGLE_COMMAND = "Require a single command"; static final String UNKNOWN_COMMAND = "Unsupported 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"; - - static final String CANCEL_INPUT_STATE = "Cancel command should have exactly one GameProposal input state"; - static final String CANCEL_OUTPUT_STATE = "Cancel command should have no output states"; - - static final String ACCEPT_INPUT_STATE = "Accept command should have exactly one GameProposal input state"; - static final String ACCEPT_OUTPUT_STATE = "Accept command should have exactly one GameBoard output state"; - static final String ACCEPT_PARTICIPANTS = "Accept command: GameBoard participants should math GameProposal participants"; -} + } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/UtxoLedgerTransactionUtil.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/UtxoLedgerTransactionUtil.java index 7e2416d..741b611 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/UtxoLedgerTransactionUtil.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/UtxoLedgerTransactionUtil.java @@ -40,6 +40,12 @@ public class UtxoLedgerTransactionUtil { return optional(utxoTrx.getOutputStates(clazz), clazz); } + public static void requireThat(boolean asserted, String errorMessage) { + if (!asserted) { + throw new IllegalStateException("Failed requirement: " + errorMessage); + } + } + private static Optional optional(List list, Class clazz) { return list .stream()