GameBoard SURRENDER update
- UtxoLedgerTrxUtil with option - rename action to command - lots of minor refectorings
This commit is contained in:
parent
d9b885b550
commit
4c2569810a
@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
||||||
import djmil.cordacheckers.cordaclient.dao.Piece;
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoardCommand;
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
||||||
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
||||||
import djmil.cordacheckers.cordaclient.dao.VirtualNodeList;
|
import djmil.cordacheckers.cordaclient.dao.VirtualNodeList;
|
||||||
@ -29,14 +30,13 @@ import djmil.cordacheckers.cordaclient.dao.flow.ResponseBody;
|
|||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateReq;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateReq;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalListRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalListRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq.Command;
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardCommandReq;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardCommandRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardListRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardListRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardMoveReq;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCommandAcceptRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardMoveRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCommandReq;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionAcceptRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCommandRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq;
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionRes;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CordaClient {
|
public class CordaClient {
|
||||||
@ -130,16 +130,16 @@ public class CordaClient {
|
|||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"gp.reject-" +UUID.randomUUID(),
|
"gp.reject-" +UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameproposal.CommandFlow",
|
"djmil.cordacheckers.gameproposal.CommandFlow",
|
||||||
new GameProposalActionReq(
|
new GameProposalCommandReq(
|
||||||
gameProposalUuid.toString(),
|
gameProposalUuid.toString(),
|
||||||
Command.REJECT
|
GameProposalCommandReq.Command.REJECT
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final GameProposalActionRes actionResult = cordaFlowExecute(
|
final GameProposalCommandRes actionResult = cordaFlowExecute(
|
||||||
myHoldingIdentity,
|
myHoldingIdentity,
|
||||||
requestBody,
|
requestBody,
|
||||||
GameProposalActionRes.class
|
GameProposalCommandRes.class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (actionResult.failureStatus() != null) {
|
if (actionResult.failureStatus() != null) {
|
||||||
@ -150,23 +150,23 @@ public class CordaClient {
|
|||||||
return actionResult.successStatus();
|
return actionResult.successStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID gameProposalAccept(
|
public UUID gameProposalAccept( // TODO shall return newGameBoard
|
||||||
HoldingIdentity myHoldingIdentity,
|
HoldingIdentity myHoldingIdentity,
|
||||||
UUID gameProposalUuid
|
UUID gameProposalUuid
|
||||||
) {
|
) {
|
||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"gp.accept-" +UUID.randomUUID(),
|
"gp.accept-" +UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameproposal.CommandFlow",
|
"djmil.cordacheckers.gameproposal.CommandFlow",
|
||||||
new GameProposalActionReq(
|
new GameProposalCommandReq(
|
||||||
gameProposalUuid.toString(),
|
gameProposalUuid.toString(),
|
||||||
Command.ACCEPT
|
GameProposalCommandReq.Command.ACCEPT
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final GameProposalActionAcceptRes actionResult = cordaFlowExecute(
|
final GameProposalCommandAcceptRes actionResult = cordaFlowExecute(
|
||||||
myHoldingIdentity,
|
myHoldingIdentity,
|
||||||
requestBody,
|
requestBody,
|
||||||
GameProposalActionAcceptRes.class
|
GameProposalCommandAcceptRes.class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (actionResult.failureStatus() != null) {
|
if (actionResult.failureStatus() != null) {
|
||||||
@ -199,24 +199,24 @@ public class CordaClient {
|
|||||||
return listFlowResult.successStatus();
|
return listFlowResult.successStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameBoard gameBoardMove(
|
public GameBoard gameBoardCommand(
|
||||||
HoldingIdentity myHoldingIdentity,
|
HoldingIdentity myHoldingIdentity,
|
||||||
UUID gameBoardUuid,
|
UUID gameBoardUuid,
|
||||||
int from, int to
|
GameBoardCommand command
|
||||||
) {
|
) {
|
||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"gb.move-" +UUID.randomUUID(),
|
"gb.command-" +command.getType() +UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameboard.MoveFlow",
|
"djmil.cordacheckers.gameboard.CommandFlow",
|
||||||
new GameBoardMoveReq(
|
new GameBoardCommandReq(
|
||||||
gameBoardUuid,
|
gameBoardUuid,
|
||||||
from, to
|
command
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final GameBoardMoveRes moveResult = cordaFlowExecute(
|
final GameBoardCommandRes moveResult = cordaFlowExecute(
|
||||||
myHoldingIdentity,
|
myHoldingIdentity,
|
||||||
requestBody,
|
requestBody,
|
||||||
GameBoardMoveRes.class
|
GameBoardCommandRes.class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (moveResult.failureStatus() != null) {
|
if (moveResult.failureStatus() != null) {
|
||||||
|
@ -11,6 +11,7 @@ public record GameBoard(
|
|||||||
Piece.Color opponentColor,
|
Piece.Color opponentColor,
|
||||||
Boolean opponentMove,
|
Boolean opponentMove,
|
||||||
Map<Integer, Piece> board,
|
Map<Integer, Piece> board,
|
||||||
|
GameBoardCommand previousCommand,
|
||||||
String message,
|
String message,
|
||||||
UUID id) implements CordaState {
|
UUID id) implements CordaState {
|
||||||
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GameBoardCommand {
|
||||||
|
public static enum Type {
|
||||||
|
MOVE,
|
||||||
|
SURRENDER,
|
||||||
|
REQUEST_DRAW,
|
||||||
|
REQUEST_VICTORY,
|
||||||
|
FINISH; // aka accept DRAW or VICTORY request
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
private final List<Integer> move; // [0] from, [1] to
|
||||||
|
|
||||||
|
public GameBoardCommand() {
|
||||||
|
this.type = null;
|
||||||
|
this.move = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameBoardCommand(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
this.move = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameBoardCommand(List<Integer> move) {
|
||||||
|
this.type = Type.MOVE;
|
||||||
|
this.move = move;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getMove() {
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (type == Type.MOVE)
|
||||||
|
return move.get(0) +"->" +move.get(1);
|
||||||
|
else
|
||||||
|
return type.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoardCommand;
|
||||||
|
|
||||||
|
public record GameBoardCommandReq(UUID gameBoardUuid, GameBoardCommand command) {
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,6 @@ package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
|||||||
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
|
||||||
public record GameBoardMoveRes (GameBoard successStatus, String failureStatus) {
|
public record GameBoardCommandRes(GameBoard successStatus, String failureStatus) {
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public record GameBoardMoveReq(UUID gameBoard, int from, int to) {
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public record GameProposalActionAcceptRes(UUID successStatus, String transactionId, String failureStatus) {
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
|
||||||
|
|
||||||
public record GameProposalActionRes(String successStatus, String transactionId, String failureStatus) {
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record GameProposalCommandAcceptRes(UUID successStatus, String transactionId, String failureStatus) {
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
public record GameProposalActionReq(String gameProposalUuid, Command command) {
|
public record GameProposalCommandReq(String gameProposalUuid, Command command) {
|
||||||
public enum Command {
|
public enum Command {
|
||||||
ACCEPT,
|
ACCEPT,
|
||||||
REJECT,
|
REJECT,
|
@ -0,0 +1,5 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
|
public record GameProposalCommandRes(String successStatus, String transactionId, String failureStatus) {
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.JsonMappingException;
|
|||||||
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.CordaState;
|
import djmil.cordacheckers.cordaclient.dao.CordaState;
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoardCommand;
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
||||||
import djmil.cordacheckers.cordaclient.dao.Piece;
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
||||||
@ -56,8 +57,7 @@ public class CordaClientTest {
|
|||||||
holdingIdentityResolver.getByUsername(gpIssuer),
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpAcquierColor,
|
gpAcquierColor,
|
||||||
gpMessage
|
gpMessage);
|
||||||
);
|
|
||||||
|
|
||||||
List<GameProposal> gpListSender = cordaClient.gameProposalList(
|
List<GameProposal> gpListSender = cordaClient.gameProposalList(
|
||||||
holdingIdentityResolver.getByUsername(gpIssuer));
|
holdingIdentityResolver.getByUsername(gpIssuer));
|
||||||
@ -87,8 +87,7 @@ public class CordaClientTest {
|
|||||||
holdingIdentityResolver.getByUsername(gpIssuer),
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpReceiverColor,
|
gpReceiverColor,
|
||||||
gpMessage
|
gpMessage);
|
||||||
);
|
|
||||||
|
|
||||||
System.out.println("Create GP UUID "+ gpUuid);
|
System.out.println("Create GP UUID "+ gpUuid);
|
||||||
|
|
||||||
@ -100,8 +99,7 @@ public class CordaClientTest {
|
|||||||
|
|
||||||
final String rejectRes = cordaClient.gameProposalReject(
|
final String rejectRes = cordaClient.gameProposalReject(
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpUuid
|
gpUuid);
|
||||||
);
|
|
||||||
|
|
||||||
assertThat(rejectRes).isEqualToIgnoringCase("REJECTED");
|
assertThat(rejectRes).isEqualToIgnoringCase("REJECTED");
|
||||||
|
|
||||||
@ -147,8 +145,7 @@ public class CordaClientTest {
|
|||||||
|
|
||||||
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
||||||
holdingIdentityResolver.getByUsername(gpAcquier),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpUuid
|
gpUuid);
|
||||||
);
|
|
||||||
|
|
||||||
System.out.println("New GameBoard UUID "+newGameBoardId);
|
System.out.println("New GameBoard UUID "+newGameBoardId);
|
||||||
|
|
||||||
@ -186,6 +183,33 @@ public class CordaClientTest {
|
|||||||
System.out.println("GB list: " +gbList);
|
System.out.println("GB list: " +gbList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGameBoardSurrender() throws JsonMappingException, JsonProcessingException {
|
||||||
|
final var hiAlice = holdingIdentityResolver.getByUsername("alice");
|
||||||
|
final var hiBob = holdingIdentityResolver.getByUsername("bob");
|
||||||
|
final var bobColor = Piece.Color.WHITE;
|
||||||
|
|
||||||
|
final UUID gpUuid = cordaClient.gameProposalCreate(
|
||||||
|
hiAlice, hiBob,
|
||||||
|
bobColor, "GameBoard SURRENDER test"
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("New GameProposal UUID "+ gpUuid);
|
||||||
|
|
||||||
|
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
||||||
|
hiBob, gpUuid
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("New GameBoard UUID "+ newGameBoardId);
|
||||||
|
|
||||||
|
GameBoard gbSurrender = cordaClient.gameBoardCommand(
|
||||||
|
hiBob, newGameBoardId,
|
||||||
|
new GameBoardCommand(GameBoardCommand.Type.SURRENDER)
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("SURRENDER GB: "+gbSurrender);
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends CordaState> T findByUuid(List<T> statesList, UUID uuid) {
|
private <T extends CordaState> T findByUuid(List<T> statesList, UUID uuid) {
|
||||||
for (T state : statesList) {
|
for (T state : statesList) {
|
||||||
if (state.id().compareTo(uuid) == 0)
|
if (state.id().compareTo(uuid) == 0)
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
package djmil.cordacheckers;
|
|
||||||
|
|
||||||
import net.corda.v5.ledger.utxo.Command;
|
|
||||||
import net.corda.v5.ledger.utxo.ContractState;
|
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
|
||||||
|
|
||||||
public class UtxoLedgerTransactionUtil {
|
|
||||||
private final UtxoLedgerTransaction trx;
|
|
||||||
|
|
||||||
public UtxoLedgerTransactionUtil(UtxoLedgerTransaction trx) {
|
|
||||||
this.trx = trx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Command> T getSingleCommand(Class<T> clazz) {
|
|
||||||
return trx.getCommands(clazz)
|
|
||||||
.stream()
|
|
||||||
.reduce( (a, b) -> {throw new IllegalStateException(trx.getId() +EXPECTED_SINGLE_CLAZZ +clazz);} )
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends ContractState> T getSingleReferenceState(Class<T> clazz) {
|
|
||||||
return trx.getReferenceStates(clazz)
|
|
||||||
.stream()
|
|
||||||
.reduce( (a, b) -> {throw new IllegalStateException(trx.getId() +EXPECTED_SINGLE_CLAZZ +clazz);} )
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends ContractState> T getSingleInputState(Class<T> clazz) {
|
|
||||||
return trx.getInputStates(clazz)
|
|
||||||
.stream()
|
|
||||||
.reduce( (a, b) -> {throw new IllegalStateException(trx.getId() +EXPECTED_SINGLE_CLAZZ +clazz);} )
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends ContractState> T getSingleOutputState(Class<T> clazz) {
|
|
||||||
return trx.getOutputStates(clazz)
|
|
||||||
.stream()
|
|
||||||
.reduce( (a, b) -> {throw new IllegalStateException(trx.getId() +EXPECTED_SINGLE_CLAZZ +clazz);} )
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String EXPECTED_SINGLE_CLAZZ = ": expected single ";
|
|
||||||
}
|
|
@ -7,15 +7,22 @@ import net.corda.v5.ledger.utxo.Command;
|
|||||||
|
|
||||||
public class GameBoardCommand implements Command {
|
public class GameBoardCommand implements Command {
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
|
MOVE,
|
||||||
SURRENDER,
|
SURRENDER,
|
||||||
DRAW,
|
REQUEST_DRAW,
|
||||||
VICTORY,
|
REQUEST_VICTORY,
|
||||||
MOVE;
|
FINISH; // aka accept DRAW or VICTORY request
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final List<Integer> move; // [0] from, [1] to
|
private final List<Integer> move; // [0] from, [1] to
|
||||||
|
|
||||||
|
// Serialisation service requires a default constructor
|
||||||
|
public GameBoardCommand() {
|
||||||
|
this.type = null;
|
||||||
|
this.move = null;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -34,12 +41,8 @@ public class GameBoardCommand implements Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getMove() {
|
public List<Integer> getMove() {
|
||||||
if (type != Type.MOVE)
|
|
||||||
throw new CordaRuntimeException (NO_MOVES_FOR_ACTIONTYPE +type);
|
|
||||||
|
|
||||||
return this.move;
|
return this.move;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String BAD_ACTIONMOVE_CONSTRUCTOR = "Bad constructor for Action.MOVE";
|
static final String BAD_ACTIONMOVE_CONSTRUCTOR = "Bad constructor for Action.MOVE";
|
||||||
static final String NO_MOVES_FOR_ACTIONTYPE = ".getMove() not possible for ";
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
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 static djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil.getSingleReferenceState;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import djmil.cordacheckers.UtxoLedgerTransactionUtil;
|
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import net.corda.v5.base.annotations.Suspendable;
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||||
@ -18,8 +22,7 @@ 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");
|
||||||
|
|
||||||
final UtxoLedgerTransactionUtil trxUtil = new UtxoLedgerTransactionUtil(trx);
|
final Command command = getSingleCommand(trx, Command.class);
|
||||||
final Command command = trxUtil.getSingleCommand(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);
|
||||||
@ -27,13 +30,15 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
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:
|
||||||
|
break;
|
||||||
case SURRENDER:
|
case SURRENDER:
|
||||||
break;
|
break;
|
||||||
case DRAW:
|
case REQUEST_DRAW:
|
||||||
break;
|
break;
|
||||||
case VICTORY:
|
case REQUEST_VICTORY:
|
||||||
break;
|
break;
|
||||||
case MOVE:
|
case FINISH:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -43,14 +48,13 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
public static GameProposalState getReferanceGameProposalState(UtxoLedgerTransaction trx) {
|
public static GameProposalState getReferanceGameProposalState(UtxoLedgerTransaction trx) {
|
||||||
final UtxoLedgerTransactionUtil trxUtil = new UtxoLedgerTransactionUtil(trx);
|
final Command command = getSingleCommand(trx, Command.class);
|
||||||
final Command command = trxUtil.getSingleCommand(Command.class);
|
|
||||||
|
|
||||||
if (command instanceof GameProposalCommand && (GameProposalCommand)command == GameProposalCommand.ACCEPT) {
|
if (command instanceof GameProposalCommand && (GameProposalCommand)command == GameProposalCommand.ACCEPT) {
|
||||||
return trxUtil.getSingleInputState(GameProposalState.class);
|
return getSingleInputState(trx, GameProposalState.class);
|
||||||
} else
|
} else
|
||||||
if (command instanceof GameBoardCommand) {
|
if (command instanceof GameBoardCommand) {
|
||||||
return trxUtil.getSingleReferenceState(GameProposalState.class);
|
return getSingleReferenceState(trx, GameProposalState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(NO_REFERANCE_GAMEPROPOSAL_STATE_FOR_TRXID +trx.getId());
|
throw new IllegalStateException(NO_REFERANCE_GAMEPROPOSAL_STATE_FOR_TRXID +trx.getId());
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
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.UtxoLedgerTransactionUtil;
|
|
||||||
import djmil.cordacheckers.states.GameBoardState;
|
import djmil.cordacheckers.states.GameBoardState;
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||||
@ -17,15 +20,14 @@ 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");
|
||||||
|
|
||||||
final UtxoLedgerTransactionUtil trxUtil = new UtxoLedgerTransactionUtil(trx);
|
final GameProposalCommand command = getSingleCommand(trx, GameProposalCommand.class);
|
||||||
final GameProposalCommand command = trxUtil.getSingleCommand(GameProposalCommand.class);
|
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case CREATE: {
|
case CREATE: {
|
||||||
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 = trxUtil.getSingleOutputState(GameProposalState.class);
|
GameProposalState outputState = getSingleOutputState(trx, GameProposalState.class);
|
||||||
|
|
||||||
requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR);
|
requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR);
|
||||||
break; }
|
break; }
|
||||||
@ -34,8 +36,8 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
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 = trxUtil.getSingleInputState(GameProposalState.class);
|
GameProposalState inGameProposal = getSingleInputState(trx, GameProposalState.class);
|
||||||
GameBoardState outGameBoard = trxUtil.getSingleOutputState(GameBoardState.class);
|
GameBoardState outGameBoard = getSingleOutputState(trx, GameBoardState.class);
|
||||||
|
|
||||||
requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), ACCEPT_PARTICIPANTS);
|
requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), ACCEPT_PARTICIPANTS);
|
||||||
break; }
|
break; }
|
||||||
@ -44,14 +46,14 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
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);
|
||||||
|
|
||||||
trxUtil.getSingleInputState(GameProposalState.class);
|
getSingleInputState(trx, GameProposalState.class);
|
||||||
break; }
|
break; }
|
||||||
|
|
||||||
case CANCEL:
|
case CANCEL:
|
||||||
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);
|
||||||
|
|
||||||
trxUtil.getSingleInputState(GameProposalState.class);
|
getSingleInputState(trx, GameProposalState.class);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package djmil.cordacheckers.contracts;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import net.corda.v5.ledger.utxo.Command;
|
||||||
|
import net.corda.v5.ledger.utxo.ContractState;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||||
|
|
||||||
|
public class UtxoLedgerTransactionUtil {
|
||||||
|
public static <T extends Command> T getSingleCommand(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return single(utxoTrx.getCommands(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> T getSingleReferenceState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return single(utxoTrx.getReferenceStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> T getSingleInputState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return single(utxoTrx.getInputStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> T getSingleOutputState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return single(utxoTrx.getOutputStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Command> Optional<T> getOptionalCommand(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return optional(utxoTrx.getCommands(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> Optional<T> getOptionalReferenceState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return optional(utxoTrx.getReferenceStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> Optional<T> getOptionalInputState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return optional(utxoTrx.getInputStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ContractState> Optional<T> getOptionalOutputState(UtxoLedgerTransaction utxoTrx, Class<T> clazz) {
|
||||||
|
return optional(utxoTrx.getOutputStates(clazz), clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> Optional<T> optional(List<T> list, Class<T> clazz) {
|
||||||
|
return list
|
||||||
|
.stream()
|
||||||
|
.reduce((a, b) -> {throw new IllegalStateException(MULTIPLE_INSTANCES_OF +clazz.getName());});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T single(List<T> list, Class<T> clazz) {
|
||||||
|
return optional(list, clazz)
|
||||||
|
.orElseThrow( () -> new IllegalStateException(NO_INSTANCES_OF +clazz.getName()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String MULTIPLE_INSTANCES_OF = "Multiple instances of ";
|
||||||
|
private static String NO_INSTANCES_OF = "No instances of ";
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package djmil.cordacheckers.gameboard;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.FlowResult;
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardCommand;
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardContract;
|
||||||
|
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||||
|
import djmil.cordacheckers.states.GameBoardState;
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
import net.corda.v5.application.flows.ClientRequestBody;
|
||||||
|
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||||
|
import net.corda.v5.application.flows.CordaInject;
|
||||||
|
import net.corda.v5.application.flows.FlowEngine;
|
||||||
|
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||||
|
import net.corda.v5.application.membership.MemberLookup;
|
||||||
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
|
import net.corda.v5.crypto.SecureHash;
|
||||||
|
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||||
|
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public class CommandFlow implements ClientStartableFlow {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(CommandFlow.class);
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public UtxoLedgerService utxoLedgerService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public FlowEngine flowEngine;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Suspendable
|
||||||
|
public String call(ClientRequestBody requestBody) {
|
||||||
|
try {
|
||||||
|
final CommandFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CommandFlowArgs.class);
|
||||||
|
//final GameCommand command = args.getCommand();
|
||||||
|
log.info("GameBoardCommandFlow: findUnconsumedGameBoardState");
|
||||||
|
final StateAndRef<GameBoardState> gbStateAndRef = findUnconsumedGameBoardState(args.getGameBoardUuid());
|
||||||
|
|
||||||
|
// final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal);
|
||||||
|
|
||||||
|
// final SecureHash trxId = this.flowEngine
|
||||||
|
// .subFlow( new CommitSubFlow(trx, command.getRespondent(utxoGameProposal)) );
|
||||||
|
|
||||||
|
// if (command == GameProposalCommand.ACCEPT) {
|
||||||
|
// GameBoardState newGb = (GameBoardState)trx.getOutputStateAndRefs().get(0).getState().getContractState();
|
||||||
|
|
||||||
|
// return new FlowResult(newGb.getId(), trxId)
|
||||||
|
// .toJsonEncodedString(jsonMarshallingService);
|
||||||
|
// }
|
||||||
|
log.info("GameBoardCommandFlow: prepareGameBoardView");
|
||||||
|
GameBoardView gbView = prepareGameBoardView(gbStateAndRef);
|
||||||
|
|
||||||
|
return new FlowResult(gbView )
|
||||||
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
log.warn("GameProposalAction flow failed to process utxo request body " + requestBody +
|
||||||
|
" because: " + e.getMessage());
|
||||||
|
return new FlowResult(e).toJsonEncodedString(jsonMarshallingService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private StateAndRef<GameBoardState> findUnconsumedGameBoardState (UUID gameBoardUuid) {
|
||||||
|
/*
|
||||||
|
* Get list of all unconsumed aka 'active' GameProposalStates, then filter by UUID.
|
||||||
|
* Note, this is an inefficient way to perform this operation if there are a large
|
||||||
|
* number of 'active' GameProposals exists in storage.
|
||||||
|
*/
|
||||||
|
return this.utxoLedgerService
|
||||||
|
.findUnconsumedStatesByType(GameBoardState.class)
|
||||||
|
.stream()
|
||||||
|
.filter(sar -> sar.getState().getContractState().getId().equals(gameBoardUuid))
|
||||||
|
.reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);})
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Suspendable
|
||||||
|
// private UtxoSignedTransaction prepareSignedTransaction(
|
||||||
|
// GameProposalCommand command,
|
||||||
|
// StateAndRef<GameProposalState> utxoGameProposal
|
||||||
|
// ) {
|
||||||
|
// UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder()
|
||||||
|
// .setNotary(utxoGameProposal.getState().getNotaryName())
|
||||||
|
// .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
|
// .addInputState(utxoGameProposal.getRef())
|
||||||
|
// .addCommand(command)
|
||||||
|
// .addSignatories(utxoGameProposal.getState().getContractState().getParticipants());
|
||||||
|
|
||||||
|
// if (command == GameProposalCommand.ACCEPT) {
|
||||||
|
// trxBuilder = trxBuilder
|
||||||
|
// .addOutputState(new GameBoardState(utxoGameProposal));
|
||||||
|
// //A state cannot be both an input and a reference input in the same transaction
|
||||||
|
// //.addReferenceState(utxoGameProposal.getRef());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return trxBuilder.toSignedTransaction();
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private GameBoardView prepareGameBoardView(StateAndRef<GameBoardState> stateAndRef) {
|
||||||
|
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
|
final SecureHash trxId = stateAndRef.getRef().getTransactionId();
|
||||||
|
|
||||||
|
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
||||||
|
.findLedgerTransaction(trxId);
|
||||||
|
log.info("GameBoardCommandFlow: createw gbView");
|
||||||
|
GameBoardView gbView = new GameBoardView(myName, utxoGameBoard);
|
||||||
|
|
||||||
|
gbView.previousCommand = new GameBoardCommand(GameBoardCommand.Type.SURRENDER);
|
||||||
|
|
||||||
|
return gbView;
|
||||||
|
// return new GameBoardView(myName, utxoGameBoard);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,24 +1,24 @@
|
|||||||
package djmil.cordacheckers.gameboard;
|
package djmil.cordacheckers.gameboard;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class MoveFlowArgs {
|
import djmil.cordacheckers.contracts.GameBoardCommand;
|
||||||
|
|
||||||
|
public class CommandFlowArgs {
|
||||||
private UUID gameBoardUuid;
|
private UUID gameBoardUuid;
|
||||||
private List<Integer> move;
|
private GameBoardCommand command;
|
||||||
|
|
||||||
// Serialisation service requires a default constructor
|
// Serialisation service requires a default constructor
|
||||||
public MoveFlowArgs() {
|
public CommandFlowArgs() {
|
||||||
this.gameBoardUuid = null;
|
this.gameBoardUuid = null;
|
||||||
this.move = null;
|
this.command = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameBoardCommand getCommand() {
|
||||||
|
return this.command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getGameBoardUuid() {
|
public UUID getGameBoardUuid() {
|
||||||
return this.gameBoardUuid;
|
return this.gameBoardUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getMove() {
|
|
||||||
return this.move;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package djmil.cordacheckers.gameboard;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardCommand;
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardContract;
|
||||||
|
import djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil;
|
||||||
|
import djmil.cordacheckers.states.GameBoardState;
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
import djmil.cordacheckers.states.Piece;
|
||||||
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||||
|
|
||||||
|
|
||||||
|
// GameBoard from the player's point of view
|
||||||
|
public class GameBoardView {
|
||||||
|
public final String opponentName;
|
||||||
|
public final Piece.Color opponentColor;
|
||||||
|
public final Boolean opponentMove;
|
||||||
|
public final Map<Integer, Piece> board;
|
||||||
|
public /*final*/ GameBoardCommand previousCommand;
|
||||||
|
public final String message;
|
||||||
|
public final UUID id;
|
||||||
|
|
||||||
|
// Serialisation service requires a default constructor
|
||||||
|
public GameBoardView() {
|
||||||
|
this.opponentName = null;
|
||||||
|
this.opponentColor = null;
|
||||||
|
this.opponentMove = null;
|
||||||
|
this.board = null;
|
||||||
|
this.previousCommand = null;
|
||||||
|
this.message = null;
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A view from a perspective of a concrete player, on a ledger transaction that has
|
||||||
|
// produced new GameBoardState
|
||||||
|
public GameBoardView(MemberX500Name myName, UtxoLedgerTransaction utxoGameBoard) {
|
||||||
|
|
||||||
|
final GameProposalState referanceGameProposal = GameBoardContract
|
||||||
|
.getReferanceGameProposalState(utxoGameBoard);
|
||||||
|
|
||||||
|
this.opponentName = referanceGameProposal.getOpponentName(myName).getCommonName();
|
||||||
|
this.opponentColor = referanceGameProposal.getOpponentColor(myName);
|
||||||
|
|
||||||
|
final GameBoardState stateGameBoard = UtxoLedgerTransactionUtil
|
||||||
|
.getSingleOutputState(utxoGameBoard, GameBoardState.class);
|
||||||
|
|
||||||
|
this.opponentMove = this.opponentColor == stateGameBoard.getMoveColor();
|
||||||
|
this.board = stateGameBoard.getBoard();
|
||||||
|
this.message = stateGameBoard.getMessage();
|
||||||
|
this.id = stateGameBoard.getId();
|
||||||
|
|
||||||
|
this.previousCommand = UtxoLedgerTransactionUtil
|
||||||
|
.getOptionalCommand(utxoGameBoard, GameBoardCommand.class)
|
||||||
|
.orElseGet(() -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,10 +43,10 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
final var unconsumedGameBoardList = utxoLedgerService
|
final var unconsumedGameBoardList = utxoLedgerService
|
||||||
.findUnconsumedStatesByType(GameBoardState.class);
|
.findUnconsumedStatesByType(GameBoardState.class);
|
||||||
|
|
||||||
List<ListItem> res = new LinkedList<ListItem>();
|
List<GameBoardView> res = new LinkedList<GameBoardView>();
|
||||||
for (StateAndRef<GameBoardState> gbState :unconsumedGameBoardList) {
|
for (StateAndRef<GameBoardState> gbState :unconsumedGameBoardList) {
|
||||||
// NOTE: lambda operations (aka .map) can not be used with UtxoLedgerService instances
|
// NOTE: lambda operations (aka .map) can not be used with UtxoLedgerService instances
|
||||||
res.add(prepareListItem(gbState));
|
res.add(prepareGameBoardView(gbState));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FlowResult(res)
|
return new FlowResult(res)
|
||||||
@ -59,7 +59,7 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private ListItem prepareListItem(StateAndRef<GameBoardState> stateAndRef) {
|
private GameBoardView prepareGameBoardView(StateAndRef<GameBoardState> stateAndRef) {
|
||||||
final MemberX500Name myName = memberLookup.myInfo().getName();
|
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
final SecureHash trxId = stateAndRef.getRef().getTransactionId();
|
final SecureHash trxId = stateAndRef.getRef().getTransactionId();
|
||||||
@ -67,14 +67,9 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
||||||
.findLedgerTransaction(trxId);
|
.findLedgerTransaction(trxId);
|
||||||
|
|
||||||
final GameProposalState referanceGameProposal = GameBoardContract
|
var newGbView = new GameBoardView(myName, utxoGameBoard);
|
||||||
.getReferanceGameProposalState(utxoGameBoard);
|
|
||||||
|
|
||||||
return new ListItem(
|
return newGbView;
|
||||||
stateAndRef.getState().getContractState(),
|
|
||||||
referanceGameProposal.getOpponentName(myName),
|
|
||||||
referanceGameProposal.getOpponentColor(myName)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package djmil.cordacheckers.gameboard;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import djmil.cordacheckers.states.GameBoardState;
|
|
||||||
import djmil.cordacheckers.states.Piece;
|
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
|
||||||
|
|
||||||
// Class to hold results of the List flow.
|
|
||||||
// JsonMarshallingService can only serialize simple classes that the underlying Jackson serializer recognises,
|
|
||||||
// hence creating a DTO style object which consists only of Strings and a UUIDs. It is possible to create custom
|
|
||||||
// serializers for the JsonMarshallingService in the future.
|
|
||||||
|
|
||||||
public class ListItem {
|
|
||||||
public final String opponentName;
|
|
||||||
public final Piece.Color opponentColor;
|
|
||||||
public final Boolean opponentMove;
|
|
||||||
public final Object board;
|
|
||||||
public final String message;
|
|
||||||
public final UUID id;
|
|
||||||
|
|
||||||
// Serialisation service requires a default constructor
|
|
||||||
public ListItem() {
|
|
||||||
this.opponentName = null;
|
|
||||||
this.opponentColor = null;
|
|
||||||
this.opponentMove = null;
|
|
||||||
this.board = null;
|
|
||||||
this.message = null;
|
|
||||||
this.id = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListItem(GameBoardState gameBoard, MemberX500Name opponentName, Piece.Color opponentColor) {
|
|
||||||
this.opponentName = opponentName.getCommonName();
|
|
||||||
this.opponentColor = opponentColor;
|
|
||||||
this.opponentMove = opponentColor == gameBoard.getMoveColor();
|
|
||||||
this.board = gameBoard.getBoard();
|
|
||||||
this.message = gameBoard.getMessage();
|
|
||||||
this.id = gameBoard.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package djmil.cordacheckers.gameboard;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import djmil.cordacheckers.FlowResult;
|
|
||||||
import djmil.cordacheckers.contracts.GameBoardCommand;
|
|
||||||
import djmil.cordacheckers.states.GameBoardState;
|
|
||||||
import net.corda.v5.application.flows.ClientRequestBody;
|
|
||||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
|
||||||
import net.corda.v5.application.flows.CordaInject;
|
|
||||||
import net.corda.v5.application.flows.FlowEngine;
|
|
||||||
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
|
||||||
import net.corda.v5.base.annotations.Suspendable;
|
|
||||||
import net.corda.v5.crypto.SecureHash;
|
|
||||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
|
||||||
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
|
||||||
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
public class MoveFlow implements ClientStartableFlow {
|
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(MoveFlow.class);
|
|
||||||
|
|
||||||
@CordaInject
|
|
||||||
public JsonMarshallingService jsonMarshallingService;
|
|
||||||
|
|
||||||
@CordaInject
|
|
||||||
public UtxoLedgerService ledgerService;
|
|
||||||
|
|
||||||
@CordaInject
|
|
||||||
public FlowEngine flowEngine;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Suspendable
|
|
||||||
public String call(ClientRequestBody requestBody) {
|
|
||||||
try {
|
|
||||||
final MoveFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, MoveFlowArgs.class);
|
|
||||||
final GameBoardCommand command = new GameBoardCommand(args.getMove());
|
|
||||||
|
|
||||||
final StateAndRef<GameBoardState> utxoGameBoard = findUnconsumedGameBoardState(args.getGameBoardUuid());
|
|
||||||
|
|
||||||
final GameBoardState newGameBoard = null; // TODO
|
|
||||||
|
|
||||||
final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameBoard, newGameBoard);
|
|
||||||
|
|
||||||
final SecureHash trxId = this.flowEngine
|
|
||||||
.subFlow( new CommitSubFlow(trx) );
|
|
||||||
|
|
||||||
return new FlowResult(newGameBoard, trxId) // TODO return players perspective GB
|
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
log.warn("GameBoardAction flow failed to process utxo request body " + requestBody +
|
|
||||||
" because: " + e.getMessage());
|
|
||||||
return new FlowResult(e).toJsonEncodedString(jsonMarshallingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
private StateAndRef<GameBoardState> findUnconsumedGameBoardState (UUID gameProposalUuid) {
|
|
||||||
/*
|
|
||||||
* Get list of all unconsumed aka 'active' GameBoardState, then filter by UUID.
|
|
||||||
* Note, this is an inefficient way to perform this operation if there are a large
|
|
||||||
* number of 'active' GameProposals exists in storage.
|
|
||||||
*/
|
|
||||||
return this.ledgerService
|
|
||||||
.findUnconsumedStatesByType(GameBoardState.class)
|
|
||||||
.stream()
|
|
||||||
.filter(sar -> sar.getState().getContractState().getId().equals(gameProposalUuid))
|
|
||||||
.reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);})
|
|
||||||
.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
private UtxoSignedTransaction prepareSignedTransaction(
|
|
||||||
GameBoardCommand command,
|
|
||||||
StateAndRef<GameBoardState> utxoGameProposal,
|
|
||||||
GameBoardState newGameBoard
|
|
||||||
) {
|
|
||||||
UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder()
|
|
||||||
.setNotary(utxoGameProposal.getState().getNotaryName())
|
|
||||||
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
|
||||||
.addInputState(utxoGameProposal.getRef())
|
|
||||||
.addOutputState(newGameBoard)
|
|
||||||
.addCommand(command)
|
|
||||||
.addSignatories(utxoGameProposal.getState().getContractState().getParticipants());
|
|
||||||
|
|
||||||
return trxBuilder.toSignedTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -44,8 +44,6 @@ 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();
|
||||||
|
|
||||||
System.out.println("Game Proposal command" + command);
|
|
||||||
|
|
||||||
final StateAndRef<GameProposalState> utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
final StateAndRef<GameProposalState> utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
||||||
|
|
||||||
final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal);
|
final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal);
|
||||||
|
Loading…
Reference in New Issue
Block a user