GameProposal ACCEPT
- create new GameBoard state - test
This commit is contained in:
parent
e235ecb942
commit
7f7722ecc0
@ -19,7 +19,8 @@ import com.fasterxml.jackson.databind.JsonMappingException;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
||||||
import djmil.cordacheckers.cordaclient.dao.Color;
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
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;
|
||||||
@ -28,7 +29,10 @@ 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.Action;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardListRes;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionAcceptRes;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionRes;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionRes;
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ public class CordaClient {
|
|||||||
public List<GameProposal> gameProposalList(HoldingIdentity holdingIdentity) {
|
public List<GameProposal> gameProposalList(HoldingIdentity holdingIdentity) {
|
||||||
|
|
||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"list-" + UUID.randomUUID(),
|
"gp.list-" + UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameproposal.ListFlow",
|
"djmil.cordacheckers.gameproposal.ListFlow",
|
||||||
new Empty()
|
new Empty()
|
||||||
);
|
);
|
||||||
@ -88,67 +92,111 @@ public class CordaClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public UUID gameProposalCreate(
|
public UUID gameProposalCreate(
|
||||||
HoldingIdentity sender,
|
HoldingIdentity issuer,
|
||||||
HoldingIdentity receiver,
|
HoldingIdentity acquier,
|
||||||
Color receiverColor,
|
Piece.Color acquierColor,
|
||||||
String message
|
String message
|
||||||
) throws JsonMappingException, JsonProcessingException {
|
) throws JsonMappingException, JsonProcessingException {
|
||||||
final GameProposalCreateReq createGameProposal = new GameProposalCreateReq(
|
|
||||||
receiver.x500Name(),
|
|
||||||
receiverColor,
|
|
||||||
message
|
|
||||||
);
|
|
||||||
|
|
||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"create-" + UUID.randomUUID(),
|
"gp.create-" + UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameproposal.CreateFlow",
|
"djmil.cordacheckers.gameproposal.CreateFlow",
|
||||||
createGameProposal
|
new GameProposalCreateReq(
|
||||||
|
acquier.x500Name(),
|
||||||
|
acquierColor,
|
||||||
|
message
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final GameProposalCreateRes createResult = cordaFlowExecute(
|
final GameProposalCreateRes createResult = cordaFlowExecute(
|
||||||
sender,
|
issuer,
|
||||||
requestBody,
|
requestBody,
|
||||||
GameProposalCreateRes.class
|
GameProposalCreateRes.class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (createResult.failureStatus() != null) {
|
if (createResult.failureStatus() != null) {
|
||||||
System.out.println("GameProposalCreateFlow failed: " + createResult.failureStatus());
|
System.out.println("GameProposal.CreateFlow failed: " + createResult.failureStatus());
|
||||||
throw new RuntimeException("GameProsal: CreateFlow execution has failed");
|
throw new RuntimeException("GameProsal: CreateFlow execution has failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return createResult.successStatus();
|
return createResult.successStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String gameProposalAction(
|
public String gameProposalReject(
|
||||||
HoldingIdentity self,
|
HoldingIdentity myHoldingIdentity,
|
||||||
UUID gameProposalUuid,
|
UUID gameProposalUuid
|
||||||
GameProposalActionReq.Action action
|
|
||||||
) {
|
) {
|
||||||
final GameProposalActionReq rejectGameProposal = new GameProposalActionReq(
|
|
||||||
gameProposalUuid.toString(),
|
|
||||||
action
|
|
||||||
);
|
|
||||||
|
|
||||||
final RequestBody requestBody = new RequestBody(
|
final RequestBody requestBody = new RequestBody(
|
||||||
"reject-" + UUID.randomUUID(),
|
"gp.reject-" +UUID.randomUUID(),
|
||||||
"djmil.cordacheckers.gameproposal.ActionFlow",
|
"djmil.cordacheckers.gameproposal.ActionFlow",
|
||||||
rejectGameProposal
|
new GameProposalActionReq(
|
||||||
|
gameProposalUuid.toString(),
|
||||||
|
Action.REJECT
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
final GameProposalActionRes actionResult = cordaFlowExecute(
|
final GameProposalActionRes actionResult = cordaFlowExecute(
|
||||||
self,
|
myHoldingIdentity,
|
||||||
requestBody,
|
requestBody,
|
||||||
GameProposalActionRes.class
|
GameProposalActionRes.class
|
||||||
);
|
);
|
||||||
|
|
||||||
if (actionResult.failureStatus() != null) {
|
if (actionResult.failureStatus() != null) {
|
||||||
System.out.println("GameProposalActionFlow failed: " + actionResult.failureStatus());
|
System.out.println("GameProposal.ActionFlow failed: " + actionResult.failureStatus());
|
||||||
throw new RuntimeException("GameProsal: ActionFlow execution has failed");
|
throw new RuntimeException("GameProsal: ActionFlow execution has failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionResult.successStatus();
|
return actionResult.successStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UUID gameProposalAccept(
|
||||||
|
HoldingIdentity myHoldingIdentity,
|
||||||
|
UUID gameProposalUuid
|
||||||
|
) {
|
||||||
|
final RequestBody requestBody = new RequestBody(
|
||||||
|
"gp.accept-" +UUID.randomUUID(),
|
||||||
|
"djmil.cordacheckers.gameproposal.ActionFlow",
|
||||||
|
new GameProposalActionReq(
|
||||||
|
gameProposalUuid.toString(),
|
||||||
|
Action.ACCEPT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final GameProposalActionAcceptRes actionResult = cordaFlowExecute(
|
||||||
|
myHoldingIdentity,
|
||||||
|
requestBody,
|
||||||
|
GameProposalActionAcceptRes.class
|
||||||
|
);
|
||||||
|
|
||||||
|
if (actionResult.failureStatus() != null) {
|
||||||
|
System.out.println("GameProposal.ActionFlow failed: " + actionResult.failureStatus());
|
||||||
|
throw new RuntimeException("GameProsal: ActionFlow execution has failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionResult.successStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GameBoard> gameBoardList(HoldingIdentity holdingIdentity) {
|
||||||
|
|
||||||
|
final RequestBody requestBody = new RequestBody(
|
||||||
|
"gb.list-" + UUID.randomUUID(),
|
||||||
|
"djmil.cordacheckers.gameboard.ListFlow",
|
||||||
|
new Empty()
|
||||||
|
);
|
||||||
|
|
||||||
|
final GameBoardListRes listFlowResult = cordaFlowExecute(
|
||||||
|
holdingIdentity,
|
||||||
|
requestBody,
|
||||||
|
GameBoardListRes.class
|
||||||
|
);
|
||||||
|
|
||||||
|
if (listFlowResult.failureStatus() != null) {
|
||||||
|
System.out.println("GameBoard.ListFlow failed: " + listFlowResult.failureStatus());
|
||||||
|
throw new RuntimeException("GameBoard: ListFlow execution has failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return listFlowResult.successStatus();
|
||||||
|
}
|
||||||
|
|
||||||
private <T> T cordaFlowExecute(HoldingIdentity holdingIdentity, RequestBody requestBody, Class<T> flowResultType) {
|
private <T> T cordaFlowExecute(HoldingIdentity holdingIdentity, RequestBody requestBody, Class<T> flowResultType) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao;
|
|
||||||
|
|
||||||
public enum Color {
|
|
||||||
WHITE,
|
|
||||||
BLACK
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface CordaState {
|
||||||
|
public UUID id();
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
|
||||||
|
@JsonDeserialize
|
||||||
|
public record GameBoard(
|
||||||
|
String opponentName,
|
||||||
|
Piece.Color opponentColor,
|
||||||
|
Boolean opponentMove,
|
||||||
|
Map<Integer, Piece> board,
|
||||||
|
String message,
|
||||||
|
UUID id) implements CordaState {
|
||||||
|
|
||||||
|
}
|
@ -8,8 +8,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|||||||
public record GameProposal(
|
public record GameProposal(
|
||||||
String issuer,
|
String issuer,
|
||||||
String acquier,
|
String acquier,
|
||||||
Color acquierColor,
|
Piece.Color acquierColor,
|
||||||
String message,
|
String message,
|
||||||
UUID id) {
|
UUID id) implements CordaState {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao;
|
||||||
|
|
||||||
|
public class Piece {
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
MAN,
|
||||||
|
KING,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Color {
|
||||||
|
WHITE,
|
||||||
|
BLACK,
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color;
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
public Piece() {
|
||||||
|
this.color = null;
|
||||||
|
this.type = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Piece(Color color, Type type) {
|
||||||
|
this.color = color;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return color +"." +type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public record GameBoardListRes(List<GameBoard> successStatus, String failureStatus) {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record GameProposalActionAcceptRes(UUID successStatus, String transactionId, String failureStatus) {
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||||
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.Color;
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
|
|
||||||
public record GameProposalCreateReq(String opponentName, Color opponentColor, String message) {
|
public record GameProposalCreateReq(String opponentName, Piece.Color opponentColor, String message) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import com.fasterxml.jackson.databind.JsonMappingException;
|
|||||||
import djmil.cordacheckers.cordaclient.CordaClient;
|
import djmil.cordacheckers.cordaclient.CordaClient;
|
||||||
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
import djmil.cordacheckers.cordaclient.dao.HoldingIdentity;
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateReq;
|
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateReq;
|
||||||
import djmil.cordacheckers.cordaclient.dao.Color;
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
||||||
import djmil.cordacheckers.user.HoldingIdentityResolver;
|
import djmil.cordacheckers.user.HoldingIdentityResolver;
|
||||||
import djmil.cordacheckers.user.User;
|
import djmil.cordacheckers.user.User;
|
||||||
@ -61,7 +61,7 @@ public class GameProposalController {
|
|||||||
final HoldingIdentity gpSender = sender.getHoldingIdentity();
|
final HoldingIdentity gpSender = sender.getHoldingIdentity();
|
||||||
// TODO: throw execption with custom type
|
// TODO: throw execption with custom type
|
||||||
final HoldingIdentity gpReceiver = holdingIdentityResolver.getByUsername(gpRequest.opponentName());
|
final HoldingIdentity gpReceiver = holdingIdentityResolver.getByUsername(gpRequest.opponentName());
|
||||||
final Color gpReceiverColor = gpRequest.opponentColor();
|
final Piece.Color gpReceiverColor = gpRequest.opponentColor();
|
||||||
|
|
||||||
// TODO handle expectionns here
|
// TODO handle expectionns here
|
||||||
UUID newGameProposalUuid = cordaClient.gameProposalCreate(
|
UUID newGameProposalUuid = cordaClient.gameProposalCreate(
|
||||||
|
@ -1,19 +1,5 @@
|
|||||||
package djmil.cordacheckers.cordaclient;
|
package djmil.cordacheckers.cordaclient;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
|
||||||
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.Color;
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
|
||||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq.Action;
|
|
||||||
import djmil.cordacheckers.user.HoldingIdentityResolver;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
@ -22,6 +8,20 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import javax.naming.InvalidNameException;
|
import javax.naming.InvalidNameException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.CordaState;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.GameProposal;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.Piece;
|
||||||
|
import djmil.cordacheckers.cordaclient.dao.VirtualNode;
|
||||||
|
import djmil.cordacheckers.user.HoldingIdentityResolver;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
public class CordaClientTest {
|
public class CordaClientTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -49,7 +49,7 @@ public class CordaClientTest {
|
|||||||
void testGemeProposalCreate() throws JsonMappingException, JsonProcessingException {
|
void testGemeProposalCreate() throws JsonMappingException, JsonProcessingException {
|
||||||
final String gpIssuer = "alice";
|
final String gpIssuer = "alice";
|
||||||
final String gpAcquier = "bob";
|
final String gpAcquier = "bob";
|
||||||
final Color gpAcquierColor = Color.WHITE;
|
final Piece.Color gpAcquierColor = Piece.Color.WHITE;
|
||||||
final String gpMessage = "GameProposal create test";
|
final String gpMessage = "GameProposal create test";
|
||||||
|
|
||||||
final UUID createdGpUuid = cordaClient.gameProposalCreate(
|
final UUID createdGpUuid = cordaClient.gameProposalCreate(
|
||||||
@ -78,58 +78,118 @@ public class CordaClientTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGemeProposalReject() throws JsonMappingException, JsonProcessingException {
|
void testGemeProposalReject() throws JsonMappingException, JsonProcessingException {
|
||||||
final String gpSender = "alice";
|
final String gpIssuer = "alice";
|
||||||
final String gpReceiver = "bob";
|
final String gpAcquier = "bob";
|
||||||
final Color gpReceiverColor = Color.WHITE;
|
final Piece.Color gpReceiverColor = Piece.Color.WHITE;
|
||||||
final String gpMessage = "GameProposal REJECT test";
|
final String gpMessage = "GameProposal REJECT test";
|
||||||
|
|
||||||
final UUID gpUuid = cordaClient.gameProposalCreate(
|
final UUID gpUuid = cordaClient.gameProposalCreate(
|
||||||
holdingIdentityResolver.getByUsername(gpSender),
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
holdingIdentityResolver.getByUsername(gpReceiver),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpReceiverColor,
|
gpReceiverColor,
|
||||||
gpMessage
|
gpMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
System.out.println("Create GP UUID "+ gpUuid);
|
System.out.println("Create GP UUID "+ gpUuid);
|
||||||
|
|
||||||
assertThatThrownBy(() -> {
|
assertThatThrownBy(() -> { // Issuer can not reject
|
||||||
cordaClient.gameProposalAction(
|
cordaClient.gameProposalReject(
|
||||||
holdingIdentityResolver.getByUsername(gpSender),
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
gpUuid,
|
gpUuid);
|
||||||
Action.REJECT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String rejectRes = cordaClient.gameProposalAction(
|
final String rejectRes = cordaClient.gameProposalReject(
|
||||||
holdingIdentityResolver.getByUsername(gpReceiver),
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
gpUuid,
|
gpUuid
|
||||||
Action.REJECT
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(rejectRes).isEqualToIgnoringCase("REJECTED");
|
assertThat(rejectRes).isEqualToIgnoringCase("REJECTED");
|
||||||
|
|
||||||
List<GameProposal> gpListSender = cordaClient.gameProposalList(
|
List<GameProposal> gpListSender = cordaClient.gameProposalList(
|
||||||
holdingIdentityResolver.getByUsername(gpSender));
|
holdingIdentityResolver.getByUsername(gpIssuer));
|
||||||
|
|
||||||
assertThat(findByUuid(gpListSender, gpUuid)).isNull();
|
assertThat(findByUuid(gpListSender, gpUuid)).isNull();
|
||||||
|
|
||||||
List<GameProposal> gpListReceiver = cordaClient.gameProposalList(
|
List<GameProposal> gpListReceiver = cordaClient.gameProposalList(
|
||||||
holdingIdentityResolver.getByUsername(gpReceiver));
|
holdingIdentityResolver.getByUsername(gpAcquier));
|
||||||
|
|
||||||
assertThat(findByUuid(gpListReceiver, gpUuid)).isNull();
|
assertThat(findByUuid(gpListReceiver, gpUuid)).isNull();
|
||||||
|
|
||||||
// GameProposal can not be rejected twice
|
// GameProposal can not be rejected twice
|
||||||
assertThatThrownBy(() -> {
|
assertThatThrownBy(() -> {
|
||||||
cordaClient.gameProposalAction(
|
cordaClient.gameProposalReject(
|
||||||
holdingIdentityResolver.getByUsername(gpSender),
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
gpUuid,
|
gpUuid);
|
||||||
Action.REJECT);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private GameProposal findByUuid(List<GameProposal> gpList, UUID uuid) {
|
@Test
|
||||||
for (GameProposal gameProposal : gpList) {
|
void testGemeProposalAccept() throws JsonMappingException, JsonProcessingException {
|
||||||
if (gameProposal.id().compareTo(uuid) == 0)
|
final String gpIssuer = "alice";
|
||||||
return gameProposal;
|
final String gpAcquier = "bob";
|
||||||
|
final Piece.Color gpAcquierColor = Piece.Color.WHITE;
|
||||||
|
final String gpMessage = "GameProposal ACCEPT test";
|
||||||
|
|
||||||
|
final UUID gpUuid = cordaClient.gameProposalCreate(
|
||||||
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
|
gpAcquierColor,
|
||||||
|
gpMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("New GameProposal UUID "+ gpUuid);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> { // Issuer can not accept
|
||||||
|
cordaClient.gameProposalAccept(
|
||||||
|
holdingIdentityResolver.getByUsername(gpIssuer),
|
||||||
|
gpUuid);
|
||||||
|
});
|
||||||
|
|
||||||
|
final UUID newGameBoardId = cordaClient.gameProposalAccept(
|
||||||
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
|
gpUuid
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("New GameBoard UUID "+newGameBoardId);
|
||||||
|
|
||||||
|
List<GameBoard> gbListIssuer = cordaClient.gameBoardList(
|
||||||
|
holdingIdentityResolver.getByUsername(gpIssuer));
|
||||||
|
|
||||||
|
GameBoard gbAlice = findByUuid(gbListIssuer, newGameBoardId);
|
||||||
|
assertThat(gbAlice).isNotNull();
|
||||||
|
assertThat(gbAlice.opponentName()).isEqualToIgnoringCase(gpAcquier);
|
||||||
|
assertThat(gbAlice.opponentColor()).isEqualByComparingTo(gpAcquierColor);
|
||||||
|
assertThat(gbAlice.opponentMove()).isEqualTo(true);
|
||||||
|
|
||||||
|
|
||||||
|
List<GameBoard> gbListAcquier = cordaClient.gameBoardList(
|
||||||
|
holdingIdentityResolver.getByUsername(gpAcquier));
|
||||||
|
|
||||||
|
GameBoard bgBob = findByUuid(gbListAcquier, newGameBoardId);
|
||||||
|
assertThat(bgBob).isNotNull();
|
||||||
|
assertThat(bgBob.opponentName()).isEqualToIgnoringCase(gpIssuer);
|
||||||
|
assertThat(bgBob.opponentColor()).isEqualByComparingTo(Piece.Color.BLACK);
|
||||||
|
assertThat(bgBob.opponentMove()).isEqualTo(false);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> { // GameProposal can not be accepted twice
|
||||||
|
cordaClient.gameProposalAccept(
|
||||||
|
holdingIdentityResolver.getByUsername(gpAcquier),
|
||||||
|
gpUuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGemeProposalList() throws JsonMappingException, JsonProcessingException {
|
||||||
|
List <GameBoard> gbList = cordaClient.gameBoardList(
|
||||||
|
holdingIdentityResolver.getByUsername("bob"));
|
||||||
|
|
||||||
|
System.out.println("GB list: " +gbList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends CordaState> T findByUuid(List<T> statesList, UUID uuid) {
|
||||||
|
for (T state : statesList) {
|
||||||
|
if (state.id().compareTo(uuid) == 0)
|
||||||
|
return state;
|
||||||
};
|
};
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package djmil.cordacheckers.contracts;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
|
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||||
|
import net.corda.v5.ledger.utxo.Command;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||||
|
|
||||||
|
import static djmil.cordacheckers.contracts.GameProposalContract.Action;
|
||||||
|
|
||||||
|
public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(GameBoardContract.class);
|
||||||
|
|
||||||
|
public static class Move implements Command { }
|
||||||
|
public static class Surrender implements Command { }
|
||||||
|
public static class ClaimVictory implements Command { }
|
||||||
|
public static class ProposeDraw implements Command { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(UtxoLedgerTransaction trx) {
|
||||||
|
log.info("GameBoardContract.verify()");
|
||||||
|
|
||||||
|
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
|
Command command = trx.getCommands().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
public static GameProposalState getReferanceGameProposalState(UtxoLedgerTransaction trx) {
|
||||||
|
final List<Command> commandList = trx.getCommands();
|
||||||
|
requireThat(commandList.size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
|
|
||||||
|
final Command command = commandList.get(0);
|
||||||
|
|
||||||
|
if (command instanceof Action && (Action)command == Action.ACCEPT) {
|
||||||
|
return trx.getInputStates(GameProposalState.class)
|
||||||
|
.stream()
|
||||||
|
.reduce( (a, b) -> {throw new IllegalStateException(SINGLE_STATE_EXPECTED);} )
|
||||||
|
.get();
|
||||||
|
|
||||||
|
} else
|
||||||
|
if (command instanceof GameBoardContract.Move)
|
||||||
|
{
|
||||||
|
List<GameProposalState> refStates = trx.getReferenceStates(GameProposalState.class);
|
||||||
|
if (refStates.size() == 1) {
|
||||||
|
return (GameProposalState) refStates.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException(NO_REFERANCE_GAMEPROPOSAL_STATE_FOR_TRXID +trx.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static 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 SINGLE_STATE_EXPECTED = "Single state expected";
|
||||||
|
static final String NO_REFERANCE_GAMEPROPOSAL_STATE_FOR_TRXID = "No reference GamePropsal state found for trx.id ";
|
||||||
|
}
|
@ -3,7 +3,7 @@ package djmil.cordacheckers.contracts;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
//import djmil.cordacheckers.states.GameProposalResolutionState;
|
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;
|
||||||
import net.corda.v5.base.types.MemberX500Name;
|
import net.corda.v5.base.types.MemberX500Name;
|
||||||
@ -47,11 +47,11 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getSender(StateAndRef<GameProposalState> utxoGameProposal) {
|
public MemberX500Name getInitiator(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||||
return getInitiator(utxoGameProposal.getState().getContractState());
|
return getInitiator(utxoGameProposal.getState().getContractState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemberX500Name getReceiver(StateAndRef<GameProposalState> utxoGameProposal) {
|
public MemberX500Name getRespondent(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||||
return getRespondent(utxoGameProposal.getState().getContractState());
|
return getRespondent(utxoGameProposal.getState().getContractState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
|
|
||||||
switch ((Action)(trx.getCommands().get(0))) {
|
switch ((trx.getCommands(Action.class).get(0))) {
|
||||||
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);
|
||||||
@ -72,12 +72,21 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
GameProposalState outputState = trx.getOutputStates(GameProposalState.class).get(0);
|
GameProposalState outputState = trx.getOutputStates(GameProposalState.class).get(0);
|
||||||
requireThat(outputState != null, CREATE_OUTPUT_STATE);
|
requireThat(outputState != null, CREATE_OUTPUT_STATE);
|
||||||
|
|
||||||
requireThat(outputState.getRecipientColor() != null, NON_NULL_RECIPIENT_COLOR);
|
requireThat(outputState.getAcquierColor() != null, NON_NULL_RECIPIENT_COLOR);
|
||||||
break; }
|
break; }
|
||||||
|
|
||||||
case ACCEPT:
|
case ACCEPT:
|
||||||
throw new CordaRuntimeException("Unimplemented!");
|
requireThat(trx.getInputContractStates().size() == 1, ACCEPT_INPUT_STATE);
|
||||||
//break;
|
requireThat(trx.getOutputContractStates().size() == 1, ACCEPT_OUTPUT_STATE);
|
||||||
|
|
||||||
|
GameProposalState inGameProposal = trx.getInputStates(GameProposalState.class).get(0);
|
||||||
|
requireThat(inGameProposal != null, ACCEPT_INPUT_STATE);
|
||||||
|
|
||||||
|
GameBoardState outGameBoard = trx.getOutputStates(GameBoardState.class).get(0);
|
||||||
|
requireThat(outGameBoard != null, ACCEPT_INPUT_STATE);
|
||||||
|
|
||||||
|
requireThat(outGameBoard.getParticipants().containsAll(inGameProposal.getParticipants()), ACCEPT_PARTICIPANTS);
|
||||||
|
break;
|
||||||
|
|
||||||
case REJECT:
|
case REJECT:
|
||||||
requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE);
|
requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE);
|
||||||
@ -114,4 +123,8 @@ public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
|||||||
|
|
||||||
static final String CANCEL_INPUT_STATE = "Cancel command should have exactly one GameProposal input state";
|
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 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";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
package djmil.cordacheckers.states;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardContract;
|
||||||
|
import djmil.cordacheckers.states.Piece.Color;
|
||||||
|
import net.corda.v5.base.annotations.ConstructorForDeserialization;
|
||||||
|
import net.corda.v5.ledger.utxo.BelongsToContract;
|
||||||
|
import net.corda.v5.ledger.utxo.ContractState;
|
||||||
|
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||||
|
|
||||||
|
@BelongsToContract(GameBoardContract.class)
|
||||||
|
public class GameBoardState implements ContractState {
|
||||||
|
|
||||||
|
Map<Integer, Piece> board;
|
||||||
|
Piece.Color moveColor;
|
||||||
|
String message;
|
||||||
|
UUID id;
|
||||||
|
List<PublicKey> participants;
|
||||||
|
|
||||||
|
public GameBoardState(
|
||||||
|
StateAndRef<GameProposalState> utxoGameBoard
|
||||||
|
) {
|
||||||
|
this.board = new LinkedHashMap<Integer, Piece>(initialBoard);
|
||||||
|
this.moveColor = Piece.Color.WHITE;
|
||||||
|
this.message = null;
|
||||||
|
this.id = UUID.randomUUID();
|
||||||
|
this.participants = utxoGameBoard.getState().getContractState().getParticipants();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConstructorForDeserialization
|
||||||
|
public GameBoardState(Map<Integer, Piece> board, Color moveColor, String message, UUID id,
|
||||||
|
List<PublicKey> participants) {
|
||||||
|
this.board = board;
|
||||||
|
this.moveColor = moveColor;
|
||||||
|
this.message = message;
|
||||||
|
this.id = id;
|
||||||
|
this.participants = participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Piece> getBoard() {
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Piece.Color getMoveColor() {
|
||||||
|
return moveColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PublicKey> getParticipants() {
|
||||||
|
return participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static Map<Integer, Piece> initialBoard = Map.ofEntries(
|
||||||
|
// Inspired by Checkers notation rules: https://www.bobnewell.net/nucleus/checkers.php
|
||||||
|
Map.entry( 1, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 2, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 3, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 4, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 5, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 6, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 7, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 8, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry( 9, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry(10, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry(11, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
Map.entry(12, new Piece(Piece.Color.BLACK, Piece.Type.MAN)),
|
||||||
|
|
||||||
|
Map.entry(21, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(22, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(23, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(24, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(25, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(26, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(27, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(28, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(29, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(30, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(31, new Piece(Piece.Color.WHITE, Piece.Type.MAN)),
|
||||||
|
Map.entry(32, new Piece(Piece.Color.WHITE, Piece.Type.MAN))
|
||||||
|
);
|
||||||
|
}
|
@ -15,24 +15,23 @@ public class GameProposalState implements ContractState {
|
|||||||
|
|
||||||
MemberX500Name issuer;
|
MemberX500Name issuer;
|
||||||
MemberX500Name acquier;
|
MemberX500Name acquier;
|
||||||
Piece.Color recipientColor;
|
Piece.Color acquierColor;
|
||||||
String message;
|
String message;
|
||||||
UUID id;
|
UUID id;
|
||||||
List<PublicKey> participants;
|
List<PublicKey> participants;
|
||||||
|
|
||||||
// Allows serialisation and to use a specified UUID
|
|
||||||
@ConstructorForDeserialization
|
@ConstructorForDeserialization
|
||||||
public GameProposalState(
|
public GameProposalState(
|
||||||
MemberX500Name issuer,
|
MemberX500Name issuer,
|
||||||
MemberX500Name acquier,
|
MemberX500Name acquier,
|
||||||
Piece.Color recipientColor,
|
Piece.Color acquierColor,
|
||||||
String message,
|
String message,
|
||||||
UUID id,
|
UUID id,
|
||||||
List<PublicKey> participants
|
List<PublicKey> participants
|
||||||
) {
|
) {
|
||||||
this.issuer = issuer;
|
this.issuer = issuer;
|
||||||
this.acquier = acquier;
|
this.acquier = acquier;
|
||||||
this.recipientColor = recipientColor;
|
this.acquierColor = acquierColor;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.participants = participants;
|
this.participants = participants;
|
||||||
@ -46,8 +45,8 @@ public class GameProposalState implements ContractState {
|
|||||||
return acquier;
|
return acquier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Piece.Color getRecipientColor() {
|
public Piece.Color getAcquierColor() {
|
||||||
return recipientColor;
|
return acquierColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
@ -61,4 +60,29 @@ public class GameProposalState implements ContractState {
|
|||||||
public List<PublicKey> getParticipants() {
|
public List<PublicKey> getParticipants() {
|
||||||
return participants;
|
return participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getWhitePlayerName() {
|
||||||
|
return acquierColor == Piece.Color.WHITE ? getAcquier() : getIssuer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getBlackPlayerName() {
|
||||||
|
return acquierColor == Piece.Color.BLACK ? getAcquier() : getIssuer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getOpponentName(MemberX500Name myName) {
|
||||||
|
if (issuer.compareTo(myName) == 0)
|
||||||
|
return acquier;
|
||||||
|
|
||||||
|
if (acquier.compareTo(myName) == 0)
|
||||||
|
return issuer;
|
||||||
|
|
||||||
|
throw new RuntimeException(myName +" seems to be not involved in " + id +" game");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Piece.Color getOpponentColor(MemberX500Name myName) {
|
||||||
|
final MemberX500Name opponentName = getOpponentName(myName);
|
||||||
|
|
||||||
|
return getWhitePlayerName().compareTo(opponentName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package djmil.cordacheckers.states;
|
package djmil.cordacheckers.states;
|
||||||
|
|
||||||
|
import net.corda.v5.base.annotations.ConstructorForDeserialization;
|
||||||
import net.corda.v5.base.annotations.CordaSerializable;
|
import net.corda.v5.base.annotations.CordaSerializable;
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
@ -20,5 +21,18 @@ public class Piece {
|
|||||||
Color color;
|
Color color;
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
|
@ConstructorForDeserialization
|
||||||
|
public Piece(Color color, Type type) {
|
||||||
|
this.color = color;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package djmil.cordacheckers.gameboard;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.FlowResult;
|
||||||
|
import djmil.cordacheckers.contracts.GameBoardContract;
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
public class ListFlow implements ClientStartableFlow {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(ListFlow.class);
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public UtxoLedgerService utxoLedgerService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
@Override
|
||||||
|
public String call(ClientRequestBody requestBody) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
final var unconsumedGameBoardList = utxoLedgerService
|
||||||
|
.findUnconsumedStatesByType(GameBoardState.class);
|
||||||
|
|
||||||
|
List<ListItem> res = new LinkedList<ListItem>();
|
||||||
|
for (StateAndRef<GameBoardState> gbState :unconsumedGameBoardList) {
|
||||||
|
// NOTE: lambda operations (aka .map) can not be used with UtxoLedgerService instances
|
||||||
|
res.add(prepareListItem(gbState));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FlowResult(res)
|
||||||
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
log.warn("GameBoard.ListFlow failed to process utxo request body " + requestBody + " because: " + e.getMessage());
|
||||||
|
return new FlowResult(e).toJsonEncodedString(jsonMarshallingService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private ListItem prepareListItem(StateAndRef<GameBoardState> stateAndRef) {
|
||||||
|
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
|
final SecureHash trxId = stateAndRef.getRef().getTransactionId();
|
||||||
|
|
||||||
|
final UtxoLedgerTransaction utxoGameBoard = utxoLedgerService
|
||||||
|
.findLedgerTransaction(trxId);
|
||||||
|
|
||||||
|
final GameProposalState referanceGameProposal = GameBoardContract
|
||||||
|
.getReferanceGameProposalState(utxoGameBoard);
|
||||||
|
|
||||||
|
return new ListItem(
|
||||||
|
stateAndRef.getState().getContractState(),
|
||||||
|
referanceGameProposal.getOpponentName(myName),
|
||||||
|
referanceGameProposal.getOpponentColor(myName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
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,13 +1,12 @@
|
|||||||
package djmil.cordacheckers.gameproposal;
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import djmil.cordacheckers.FlowResult;
|
import djmil.cordacheckers.FlowResult;
|
||||||
|
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;
|
||||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
import net.corda.v5.application.flows.ClientStartableFlow;
|
||||||
@ -15,7 +14,6 @@ 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.base.annotations.Suspendable;
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
|
||||||
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;
|
||||||
@ -47,12 +45,19 @@ public class ActionFlow implements ClientStartableFlow {
|
|||||||
final ActionFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, ActionFlowArgs.class);
|
final ActionFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, ActionFlowArgs.class);
|
||||||
final Action action = args.getAction();
|
final Action action = args.getAction();
|
||||||
|
|
||||||
final StateAndRef<GameProposalState> inputState = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
final StateAndRef<GameProposalState> utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
||||||
|
|
||||||
final UtxoSignedTransaction trx = prepareSignedTransaction(action, inputState);
|
final UtxoSignedTransaction trx = prepareSignedTransaction(action, utxoGameProposal);
|
||||||
|
|
||||||
final SecureHash trxId = this.flowEngine
|
final SecureHash trxId = this.flowEngine
|
||||||
.subFlow( new CommitSubFlow(trx, action.getReceiver(inputState)) );
|
.subFlow( new CommitSubFlow(trx, action.getRespondent(utxoGameProposal)) );
|
||||||
|
|
||||||
|
if (action == Action.ACCEPT) {
|
||||||
|
GameBoardState newGb = (GameBoardState)trx.getOutputStateAndRefs().get(0).getState().getContractState();
|
||||||
|
|
||||||
|
return new FlowResult(newGb.getId(), trxId)
|
||||||
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
|
}
|
||||||
|
|
||||||
return new FlowResult(action+"ED", trxId) // REJECT+ED
|
return new FlowResult(action+"ED", trxId) // REJECT+ED
|
||||||
.toJsonEncodedString(jsonMarshallingService);
|
.toJsonEncodedString(jsonMarshallingService);
|
||||||
@ -71,36 +76,31 @@ public class ActionFlow implements ClientStartableFlow {
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
List<StateAndRef<GameProposalState>> stateAndRefs = this.ledgerService
|
return this.ledgerService
|
||||||
.findUnconsumedStatesByType(GameProposalState.class)
|
.findUnconsumedStatesByType(GameProposalState.class)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(sar -> sar.getState().getContractState().getId().equals(gameProposalUuid))
|
.filter(sar -> sar.getState().getContractState().getId().equals(gameProposalUuid))
|
||||||
.collect(Collectors.toList());
|
.reduce((a, b) -> {throw new IllegalStateException("Multiple states: " +a +", " +b);})
|
||||||
|
.get();
|
||||||
if (stateAndRefs.size() != 1) {
|
|
||||||
throw new CordaRuntimeException("Expected only one GameProposal state with id " + gameProposalUuid +
|
|
||||||
", but found " + stateAndRefs.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateAndRefs.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private UtxoSignedTransaction prepareSignedTransaction(
|
private UtxoSignedTransaction prepareSignedTransaction(
|
||||||
Action action,
|
Action action,
|
||||||
StateAndRef<GameProposalState> inputState
|
StateAndRef<GameProposalState> utxoGameProposal
|
||||||
) {
|
) {
|
||||||
UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder()
|
UtxoTransactionBuilder trxBuilder = ledgerService.createTransactionBuilder()
|
||||||
.setNotary(inputState.getState().getNotaryName())
|
.setNotary(utxoGameProposal.getState().getNotaryName())
|
||||||
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
.addInputState(inputState.getRef())
|
.addInputState(utxoGameProposal.getRef())
|
||||||
.addCommand(action)
|
.addCommand(action)
|
||||||
.addSignatories(inputState.getState().getContractState().getParticipants());
|
.addSignatories(utxoGameProposal.getState().getContractState().getParticipants());
|
||||||
|
|
||||||
if (action == Action.ACCEPT) {
|
if (action == Action.ACCEPT) {
|
||||||
// TODO
|
trxBuilder = trxBuilder
|
||||||
// .addOutputState(outputState)
|
.addOutputState(new GameBoardState(utxoGameProposal));
|
||||||
throw new RuntimeException(action +" unimplemented");
|
//A state cannot be both an input and a reference input in the same transaction
|
||||||
|
//.addReferenceState(utxoGameProposal.getRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
return trxBuilder.toSignedTransaction();
|
return trxBuilder.toSignedTransaction();
|
||||||
|
@ -21,6 +21,7 @@ import static djmil.cordacheckers.contracts.GameProposalContract.Action;
|
|||||||
|
|
||||||
@InitiatedBy(protocol = "game-proposal")
|
@InitiatedBy(protocol = "game-proposal")
|
||||||
public class CommitResponderFlow implements ResponderFlow {
|
public class CommitResponderFlow implements ResponderFlow {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(CommitResponderFlow.class);
|
private final static Logger log = LoggerFactory.getLogger(CommitResponderFlow.class);
|
||||||
|
|
||||||
@CordaInject
|
@CordaInject
|
||||||
@ -38,7 +39,7 @@ public class CommitResponderFlow implements ResponderFlow {
|
|||||||
|
|
||||||
final GameProposalState gameProposal = getGameProposal(ledgerTransaction);
|
final GameProposalState gameProposal = getGameProposal(ledgerTransaction);
|
||||||
|
|
||||||
checkSessionParticipants(session, gameProposal, action);
|
checkParticipants(session, gameProposal, action);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Other checks / actions ?
|
* Other checks / actions ?
|
||||||
@ -59,7 +60,7 @@ public class CommitResponderFlow implements ResponderFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
void checkSessionParticipants(
|
void checkParticipants(
|
||||||
FlowSession session,
|
FlowSession session,
|
||||||
GameProposalState gameProposal,
|
GameProposalState gameProposal,
|
||||||
Action action
|
Action action
|
||||||
@ -90,6 +91,6 @@ public class CommitResponderFlow implements ResponderFlow {
|
|||||||
default:
|
default:
|
||||||
throw new RuntimeException(Action.UNSUPPORTED_ACTION_VALUE_OF +action);
|
throw new RuntimeException(Action.UNSUPPORTED_ACTION_VALUE_OF +action);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import net.corda.v5.application.flows.ClientRequestBody;
|
|||||||
import net.corda.v5.application.flows.ClientStartableFlow;
|
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.flows.InitiatingFlow;
|
|
||||||
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.application.membership.MemberLookup;
|
||||||
import net.corda.v5.base.annotations.Suspendable;
|
import net.corda.v5.base.annotations.Suspendable;
|
||||||
|
@ -29,7 +29,7 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
@Override
|
@Override
|
||||||
public String call(ClientRequestBody requestBody) {
|
public String call(ClientRequestBody requestBody) {
|
||||||
try {
|
try {
|
||||||
log.info("ListChatsFlow.call() called");
|
log.info("ListFlow.call() called");
|
||||||
|
|
||||||
// Queries the VNode's vault for unconsumed states and converts the resulting
|
// Queries the VNode's vault for unconsumed states and converts the resulting
|
||||||
// List<StateAndRef<GameProposalState>> to a _serializable_ List<ListItem> DTO
|
// List<StateAndRef<GameProposalState>> to a _serializable_ List<ListItem> DTO
|
||||||
@ -41,7 +41,7 @@ public class ListFlow implements ClientStartableFlow {
|
|||||||
|
|
||||||
return new FlowResult(unconsumedGameProposaList).toJsonEncodedString(jsonMarshallingService);
|
return new FlowResult(unconsumedGameProposaList).toJsonEncodedString(jsonMarshallingService);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("CreateGameProposal flow failed to process utxo request body " + requestBody + " because: " + e.getMessage());
|
log.warn("ListFlow failed to process utxo request body " + requestBody + " because: " + e.getMessage());
|
||||||
return new FlowResult(e).toJsonEncodedString(jsonMarshallingService);
|
return new FlowResult(e).toJsonEncodedString(jsonMarshallingService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,17 @@ package djmil.cordacheckers.gameproposal;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import djmil.cordacheckers.states.GameProposalState;
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
import djmil.cordacheckers.states.Piece;
|
||||||
|
|
||||||
// Class to hold results of the List flow.
|
// Class to hold results of the List flow.
|
||||||
// The GameProposal(s) cannot be returned directly as the JsonMarshallingService can only serialize simple classes
|
// JsonMarshallingService can only serialize simple classes that the underlying Jackson serializer recognises,
|
||||||
// that the underlying Jackson serializer recognises, hence creating a DTO style object which consists only of Strings
|
// hence creating a DTO style object which consists only of Strings and a UUIDs. It is possible to create custom
|
||||||
// and a UUID. It is possible to create custom serializers for the JsonMarshallingService in the future.
|
// serializers for the JsonMarshallingService in the future.
|
||||||
|
|
||||||
public class ListItem {
|
public class ListItem {
|
||||||
public final String issuer;
|
public final String issuer;
|
||||||
public final String acquier;
|
public final String acquier;
|
||||||
public final String acquierColor;
|
public final Piece.Color acquierColor;
|
||||||
public final String message;
|
public final String message;
|
||||||
public final UUID id;
|
public final UUID id;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ public class ListItem {
|
|||||||
public ListItem(GameProposalState state) {
|
public ListItem(GameProposalState state) {
|
||||||
this.issuer = state.getIssuer().getCommonName();
|
this.issuer = state.getIssuer().getCommonName();
|
||||||
this.acquier = state.getAcquier().getCommonName();
|
this.acquier = state.getAcquier().getCommonName();
|
||||||
this.acquierColor = state.getRecipientColor().name();
|
this.acquierColor = state.getAcquierColor();
|
||||||
this.message = state.getMessage();
|
this.message = state.getMessage();
|
||||||
this.id = state.getId();
|
this.id = state.getId();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user