Compare commits
2 Commits
4c2569810a
...
d7b6ce1f25
Author | SHA1 | Date | |
---|---|---|---|
d7b6ce1f25 | |||
a836d14fbd |
@ -150,7 +150,7 @@ public class CordaClient {
|
|||||||
return actionResult.successStatus();
|
return actionResult.successStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID gameProposalAccept( // TODO shall return newGameBoard
|
public GameBoard gameProposalAccept(
|
||||||
HoldingIdentity myHoldingIdentity,
|
HoldingIdentity myHoldingIdentity,
|
||||||
UUID gameProposalUuid
|
UUID gameProposalUuid
|
||||||
) {
|
) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
import java.util.UUID;
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
|
||||||
public record GameProposalCommandAcceptRes(UUID successStatus, String transactionId, String failureStatus) {
|
public record GameProposalCommandAcceptRes(GameBoard successStatus, String transactionId, String failureStatus) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -143,16 +143,16 @@ public class CordaClientTest {
|
|||||||
gpUuid);
|
gpUuid);
|
||||||
});
|
});
|
||||||
|
|
||||||
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
final GameBoard gbState = cordaClient.gameProposalAccept(
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpUuid);
|
gpUuid);
|
||||||
|
|
||||||
System.out.println("New GameBoard UUID "+newGameBoardId);
|
System.out.println("New GameBoard UUID "+gbState);
|
||||||
|
|
||||||
List<GameBoard> gbListIssuer = cordaClient.gameBoardList(
|
List<GameBoard> gbListIssuer = cordaClient.gameBoardList(
|
||||||
holdingIdentityResolver.getByUsername(gpIssuer));
|
holdingIdentityResolver.getByUsername(gpIssuer));
|
||||||
|
|
||||||
GameBoard gbAlice = findByUuid(gbListIssuer, newGameBoardId);
|
GameBoard gbAlice = findByUuid(gbListIssuer, gbState.id());
|
||||||
assertThat(gbAlice).isNotNull();
|
assertThat(gbAlice).isNotNull();
|
||||||
assertThat(gbAlice.opponentName()).isEqualToIgnoringCase(gpAcquier);
|
assertThat(gbAlice.opponentName()).isEqualToIgnoringCase(gpAcquier);
|
||||||
assertThat(gbAlice.opponentColor()).isEqualByComparingTo(gpAcquierColor);
|
assertThat(gbAlice.opponentColor()).isEqualByComparingTo(gpAcquierColor);
|
||||||
@ -162,7 +162,7 @@ public class CordaClientTest {
|
|||||||
List<GameBoard> gbListAcquier = cordaClient.gameBoardList(
|
List<GameBoard> gbListAcquier = cordaClient.gameBoardList(
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier));
|
holdingIdentityResolver.getByUsername(gpAcquier));
|
||||||
|
|
||||||
GameBoard bgBob = findByUuid(gbListAcquier, newGameBoardId);
|
GameBoard bgBob = findByUuid(gbListAcquier, gbState.id());
|
||||||
assertThat(bgBob).isNotNull();
|
assertThat(bgBob).isNotNull();
|
||||||
assertThat(bgBob.opponentName()).isEqualToIgnoringCase(gpIssuer);
|
assertThat(bgBob.opponentName()).isEqualToIgnoringCase(gpIssuer);
|
||||||
assertThat(bgBob.opponentColor()).isEqualByComparingTo(Piece.Color.BLACK);
|
assertThat(bgBob.opponentColor()).isEqualByComparingTo(Piece.Color.BLACK);
|
||||||
@ -196,14 +196,14 @@ public class CordaClientTest {
|
|||||||
|
|
||||||
System.out.println("New GameProposal UUID "+ gpUuid);
|
System.out.println("New GameProposal UUID "+ gpUuid);
|
||||||
|
|
||||||
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
final GameBoard gbState = cordaClient.gameProposalAccept(
|
||||||
hiBob, gpUuid
|
hiBob, gpUuid
|
||||||
);
|
);
|
||||||
|
|
||||||
System.out.println("New GameBoard UUID "+ newGameBoardId);
|
System.out.println("New GameBoard UUID "+ gbState.id());
|
||||||
|
|
||||||
GameBoard gbSurrender = cordaClient.gameBoardCommand(
|
final GameBoard gbSurrender = cordaClient.gameBoardCommand(
|
||||||
hiBob, newGameBoardId,
|
hiBob, gbState.id(),
|
||||||
new GameBoardCommand(GameBoardCommand.Type.SURRENDER)
|
new GameBoardCommand(GameBoardCommand.Type.SURRENDER)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,16 +2,20 @@ package djmil.cordacheckers.contracts;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.corda.v5.base.annotations.ConstructorForDeserialization;
|
||||||
|
import net.corda.v5.base.annotations.CordaSerializable;
|
||||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||||
import net.corda.v5.ledger.utxo.Command;
|
import net.corda.v5.ledger.utxo.Command;
|
||||||
|
|
||||||
public class GameBoardCommand implements Command {
|
public class GameBoardCommand implements Command {
|
||||||
|
|
||||||
|
@CordaSerializable
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
MOVE,
|
MOVE,
|
||||||
SURRENDER,
|
SURRENDER,
|
||||||
REQUEST_DRAW,
|
DRAW,
|
||||||
REQUEST_VICTORY,
|
VICTORY,
|
||||||
FINISH; // aka accept DRAW or VICTORY request
|
ACCEPT; // aka accept opponents DRAW or VICTORY request
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
@ -23,6 +27,12 @@ public class GameBoardCommand implements Command {
|
|||||||
this.move = null;
|
this.move = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConstructorForDeserialization
|
||||||
|
public GameBoardCommand(Type type, List<Integer> move) {
|
||||||
|
this.type = type;
|
||||||
|
this.move = move;
|
||||||
|
}
|
||||||
|
|
||||||
public GameBoardCommand(Type type) {
|
public GameBoardCommand(Type type) {
|
||||||
if (type == Type.MOVE)
|
if (type == Type.MOVE)
|
||||||
throw new CordaRuntimeException (BAD_ACTIONMOVE_CONSTRUCTOR);
|
throw new CordaRuntimeException (BAD_ACTIONMOVE_CONSTRUCTOR);
|
||||||
|
@ -22,23 +22,34 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
public void verify(UtxoLedgerTransaction trx) {
|
public void verify(UtxoLedgerTransaction trx) {
|
||||||
log.info("GameBoardContract.verify() called");
|
log.info("GameBoardContract.verify() called");
|
||||||
|
|
||||||
|
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
final Command command = getSingleCommand(trx, Command.class);
|
final Command command = getSingleCommand(trx, Command.class);
|
||||||
|
|
||||||
if (command instanceof GameProposalCommand) {
|
if (command instanceof GameProposalCommand) {
|
||||||
log.info("GameBoardContract.verify() as GameProposalCommand "+(GameProposalCommand)command);
|
log.info("GameBoardContract.verify() as GameProposalCommand "+(GameProposalCommand)command);
|
||||||
|
switch ((GameProposalCommand)command) {
|
||||||
|
case ACCEPT:
|
||||||
|
GameProposalCommand.validateAcceptTrx(trx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new CordaRuntimeException(UNKNOWN_COMMAND);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
if (command instanceof GameBoardCommand) {
|
if (command instanceof GameBoardCommand) {
|
||||||
log.info("GameBoardContract.verify() as GameBoardCommand "+((GameBoardCommand)command).getType());
|
log.info("GameBoardContract.verify() as GameBoardCommand "+((GameBoardCommand)command).getType());
|
||||||
switch (((GameBoardCommand)command).getType()) {
|
switch (((GameBoardCommand)command).getType()) {
|
||||||
case MOVE:
|
case MOVE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SURRENDER:
|
case SURRENDER:
|
||||||
break;
|
break;
|
||||||
case REQUEST_DRAW:
|
|
||||||
|
case DRAW:
|
||||||
break;
|
break;
|
||||||
case REQUEST_VICTORY:
|
case VICTORY:
|
||||||
break;
|
break;
|
||||||
case FINISH:
|
case ACCEPT:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
package djmil.cordacheckers.contracts;
|
package djmil.cordacheckers.contracts;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.states.GameBoardState;
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
import net.corda.v5.ledger.utxo.Command;
|
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 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 {
|
public enum GameProposalCommand implements Command {
|
||||||
CREATE,
|
CREATE,
|
||||||
@ -45,5 +51,52 @@ public enum GameProposalCommand implements Command {
|
|||||||
return getRespondent(utxoGameProposal.getState().getContractState());
|
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: ";
|
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";
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
package djmil.cordacheckers.contracts;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.base.exceptions.CordaRuntimeException;
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
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 {
|
public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(GameProposalContract.class);
|
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) {
|
public void verify(UtxoLedgerTransaction trx) {
|
||||||
log.info("GameProposalContract.verify() called");
|
log.info("GameProposalContract.verify() called");
|
||||||
|
|
||||||
|
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
final GameProposalCommand command = getSingleCommand(trx, GameProposalCommand.class);
|
final GameProposalCommand command = getSingleCommand(trx, GameProposalCommand.class);
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CREATE: {
|
case CREATE:
|
||||||
requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE);
|
GameProposalCommand.validateCreateTrx(trx);
|
||||||
requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE);
|
break;
|
||||||
|
|
||||||
GameProposalState outputState = getSingleOutputState(trx, GameProposalState.class);
|
case ACCEPT:
|
||||||
|
GameProposalCommand.validateAcceptTrx(trx);
|
||||||
|
break;
|
||||||
|
|
||||||
requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR);
|
case REJECT:
|
||||||
break; }
|
GameProposalCommand.validateRejectTrx(trx);
|
||||||
|
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 CANCEL:
|
case CANCEL:
|
||||||
requireThat(trx.getInputContractStates().size() == 1, CANCEL_INPUT_STATE);
|
GameProposalCommand.validateCancelTrx(trx);
|
||||||
requireThat(trx.getOutputContractStates().isEmpty(), CANCEL_OUTPUT_STATE);
|
|
||||||
|
|
||||||
getSingleInputState(trx, GameProposalState.class);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new CordaRuntimeException(UNKNOWN_COMMAND);
|
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 REQUIRE_SINGLE_COMMAND = "Require a single command";
|
||||||
static final String UNKNOWN_COMMAND = "Unsupported 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";
|
|
||||||
}
|
|
||||||
|
@ -40,6 +40,12 @@ public class UtxoLedgerTransactionUtil {
|
|||||||
return optional(utxoTrx.getOutputStates(clazz), clazz);
|
return optional(utxoTrx.getOutputStates(clazz), clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void requireThat(boolean asserted, String errorMessage) {
|
||||||
|
if (!asserted) {
|
||||||
|
throw new IllegalStateException("Failed requirement: " + errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> Optional<T> optional(List<T> list, Class<T> clazz) {
|
private static <T> Optional<T> optional(List<T> list, Class<T> clazz) {
|
||||||
return list
|
return list
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -67,9 +67,7 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
||||||
.findLedgerTransaction(trxId);
|
.findLedgerTransaction(trxId);
|
||||||
|
|
||||||
var newGbView = new GameBoardView(myName, utxoGameBoard);
|
return new GameBoardView(myName, utxoGameBoard);
|
||||||
|
|
||||||
return newGbView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import djmil.cordacheckers.FlowResult;
|
import djmil.cordacheckers.FlowResult;
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardCommand;
|
||||||
import djmil.cordacheckers.contracts.GameProposalCommand;
|
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||||
|
import djmil.cordacheckers.gameboard.GameBoardView;
|
||||||
import djmil.cordacheckers.states.GameBoardState;
|
import djmil.cordacheckers.states.GameBoardState;
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import net.corda.v5.application.flows.ClientRequestBody;
|
import net.corda.v5.application.flows.ClientRequestBody;
|
||||||
@ -14,10 +16,13 @@ import net.corda.v5.application.flows.ClientStartableFlow;
|
|||||||
import net.corda.v5.application.flows.CordaInject;
|
import net.corda.v5.application.flows.CordaInject;
|
||||||
import net.corda.v5.application.flows.FlowEngine;
|
import net.corda.v5.application.flows.FlowEngine;
|
||||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||||
|
import net.corda.v5.application.membership.MemberLookup;
|
||||||
import net.corda.v5.base.annotations.Suspendable;
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
import net.corda.v5.crypto.SecureHash;
|
import net.corda.v5.crypto.SecureHash;
|
||||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||||
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
||||||
|
|
||||||
@ -32,11 +37,14 @@ public class CommandFlow implements ClientStartableFlow {
|
|||||||
public JsonMarshallingService jsonMarshallingService;
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public UtxoLedgerService ledgerService;
|
public UtxoLedgerService utxoLedgerService;
|
||||||
|
|
||||||
@CordaInject
|
@CordaInject
|
||||||
public FlowEngine flowEngine;
|
public FlowEngine flowEngine;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Suspendable
|
@Suspendable
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
@ -44,17 +52,17 @@ public class CommandFlow implements ClientStartableFlow {
|
|||||||
final CommandFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CommandFlowArgs.class);
|
final CommandFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CommandFlowArgs.class);
|
||||||
final GameProposalCommand command = args.getCommand();
|
final GameProposalCommand command = args.getCommand();
|
||||||
|
|
||||||
final StateAndRef<GameProposalState> utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
final StateAndRef<GameProposalState> gpStateAndRef = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
||||||
|
|
||||||
final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal);
|
final UtxoSignedTransaction trxCandidate = prepareSignedTransaction(command, gpStateAndRef);
|
||||||
|
|
||||||
final SecureHash trxId = this.flowEngine
|
final SecureHash trxId = this.flowEngine
|
||||||
.subFlow( new CommitSubFlow(trx, command.getRespondent(utxoGameProposal)) );
|
.subFlow( new CommitSubFlow(trxCandidate, command.getRespondent(gpStateAndRef)) );
|
||||||
|
|
||||||
if (command == GameProposalCommand.ACCEPT) {
|
if (command == GameProposalCommand.ACCEPT) {
|
||||||
GameBoardState newGb = (GameBoardState)trx.getOutputStateAndRefs().get(0).getState().getContractState();
|
final GameBoardView gbView = prepareGameBoardView(trxId);
|
||||||
|
|
||||||
return new FlowResult(newGb.getId(), trxId)
|
return new FlowResult(gbView, trxId)
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,16 +77,16 @@ public class CommandFlow implements ClientStartableFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private StateAndRef<GameProposalState> findUnconsumedGameProposalState (UUID gameProposalUuid) {
|
private StateAndRef<GameProposalState> findUnconsumedGameProposalState (UUID gpUuid) {
|
||||||
/*
|
/*
|
||||||
* Get list of all unconsumed aka 'active' GameProposalStates, then filter by UUID.
|
* Get list of all unconsumed aka 'active' GameProposalStates, then filter by UUID.
|
||||||
* Note, this is an inefficient way to perform this operation if there are a large
|
* Note, this is an inefficient way to perform this operation if there are a large
|
||||||
* number of 'active' GameProposals exists in storage.
|
* number of 'active' GameProposals exists in storage.
|
||||||
*/
|
*/
|
||||||
return this.ledgerService
|
return this.utxoLedgerService
|
||||||
.findUnconsumedStatesByType(GameProposalState.class)
|
.findUnconsumedStatesByType(GameProposalState.class)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(sar -> sar.getState().getContractState().getId().equals(gameProposalUuid))
|
.filter(sar -> sar.getState().getContractState().getId().equals(gpUuid))
|
||||||
.reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);})
|
.reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);})
|
||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
@ -86,18 +94,18 @@ public class CommandFlow implements ClientStartableFlow {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
private UtxoSignedTransaction prepareSignedTransaction(
|
private UtxoSignedTransaction prepareSignedTransaction(
|
||||||
GameProposalCommand command,
|
GameProposalCommand command,
|
||||||
StateAndRef<GameProposalState> utxoGameProposal
|
StateAndRef<GameProposalState> gpStateAndRef
|
||||||
) {
|
) {
|
||||||
UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder()
|
UtxoTransactionBuilder trxBuilder = utxoLedgerService.createTransactionBuilder()
|
||||||
.setNotary(utxoGameProposal.getState().getNotaryName())
|
.setNotary(gpStateAndRef.getState().getNotaryName())
|
||||||
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.addInputState(utxoGameProposal.getRef())
|
.addInputState(gpStateAndRef.getRef())
|
||||||
.addCommand(command)
|
.addCommand(command)
|
||||||
.addSignatories(utxoGameProposal.getState().getContractState().getParticipants());
|
.addSignatories(gpStateAndRef.getState().getContractState().getParticipants());
|
||||||
|
|
||||||
if (command == GameProposalCommand.ACCEPT) {
|
if (command == GameProposalCommand.ACCEPT) {
|
||||||
trxBuilder = trxBuilder
|
trxBuilder = trxBuilder
|
||||||
.addOutputState(new GameBoardState(utxoGameProposal));
|
.addOutputState(new GameBoardState(gpStateAndRef));
|
||||||
//A state cannot be both an input and a reference input in the same transaction
|
//A state cannot be both an input and a reference input in the same transaction
|
||||||
//.addReferenceState(utxoGameProposal.getRef());
|
//.addReferenceState(utxoGameProposal.getRef());
|
||||||
}
|
}
|
||||||
@ -105,4 +113,14 @@ public class CommandFlow implements ClientStartableFlow {
|
|||||||
return trxBuilder.toSignedTransaction();
|
return trxBuilder.toSignedTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private GameBoardView prepareGameBoardView(SecureHash gbUtxoTrxId) {
|
||||||
|
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
|
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
||||||
|
.findLedgerTransaction(gbUtxoTrxId);
|
||||||
|
|
||||||
|
return new GameBoardView(myName, utxoGameBoard);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user