Move Contracts to individual classes
initial implementation for GameBoardMove
This commit is contained in:
parent
7f7722ecc0
commit
01fd273c3a
@ -29,9 +29,11 @@ import djmil.cordacheckers.cordaclient.dao.flow.ResponseBody;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateReq;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalCreateRes;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalListRes;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq.Action;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq.Command;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.Empty;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardListRes;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardMoveReq;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameBoardMoveRes;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionAcceptRes;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionReq;
|
||||
import djmil.cordacheckers.cordaclient.dao.flow.arguments.GameProposalActionRes;
|
||||
@ -66,10 +68,10 @@ public class CordaClient {
|
||||
|
||||
/**
|
||||
* Obtain list of unconsumed (active) GameProposals
|
||||
* @param holdingIdentity
|
||||
* @param myHoldingIdentity
|
||||
* @return GameProposals list in JSON form
|
||||
*/
|
||||
public List<GameProposal> gameProposalList(HoldingIdentity holdingIdentity) {
|
||||
public List<GameProposal> gameProposalList(HoldingIdentity myHoldingIdentity) {
|
||||
|
||||
final RequestBody requestBody = new RequestBody(
|
||||
"gp.list-" + UUID.randomUUID(),
|
||||
@ -78,7 +80,7 @@ public class CordaClient {
|
||||
);
|
||||
|
||||
final GameProposalListRes listFlowResult = cordaFlowExecute(
|
||||
holdingIdentity,
|
||||
myHoldingIdentity,
|
||||
requestBody,
|
||||
GameProposalListRes.class
|
||||
);
|
||||
@ -130,7 +132,7 @@ public class CordaClient {
|
||||
"djmil.cordacheckers.gameproposal.ActionFlow",
|
||||
new GameProposalActionReq(
|
||||
gameProposalUuid.toString(),
|
||||
Action.REJECT
|
||||
Command.REJECT
|
||||
)
|
||||
);
|
||||
|
||||
@ -157,7 +159,7 @@ public class CordaClient {
|
||||
"djmil.cordacheckers.gameproposal.ActionFlow",
|
||||
new GameProposalActionReq(
|
||||
gameProposalUuid.toString(),
|
||||
Action.ACCEPT
|
||||
Command.ACCEPT
|
||||
)
|
||||
);
|
||||
|
||||
@ -197,6 +199,34 @@ public class CordaClient {
|
||||
return listFlowResult.successStatus();
|
||||
}
|
||||
|
||||
public GameBoard gameBoardMove(
|
||||
HoldingIdentity myHoldingIdentity,
|
||||
UUID gameBoardUuid,
|
||||
int from, int to
|
||||
) {
|
||||
final RequestBody requestBody = new RequestBody(
|
||||
"gb.move-" +UUID.randomUUID(),
|
||||
"djmil.cordacheckers.gameboard.MoveFlow",
|
||||
new GameBoardMoveReq(
|
||||
gameBoardUuid,
|
||||
from, to
|
||||
)
|
||||
);
|
||||
|
||||
final GameBoardMoveRes moveResult = cordaFlowExecute(
|
||||
myHoldingIdentity,
|
||||
requestBody,
|
||||
GameBoardMoveRes.class
|
||||
);
|
||||
|
||||
if (moveResult.failureStatus() != null) {
|
||||
System.out.println("GameBoard.MoveFlow failed: " + moveResult.failureStatus());
|
||||
throw new RuntimeException("GameBoard: MoveFlow execution has failed");
|
||||
}
|
||||
|
||||
return moveResult.successStatus();
|
||||
}
|
||||
|
||||
private <T> T cordaFlowExecute(HoldingIdentity holdingIdentity, RequestBody requestBody, Class<T> flowResultType) {
|
||||
|
||||
try {
|
||||
@ -283,4 +313,5 @@ public class CordaClient {
|
||||
|
||||
throw new RuntimeException ("CordaClient.cordaFlowPoll: retry limit");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record GameBoardMoveReq(UUID gameBoard, int from, int to) {
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||
|
||||
import djmil.cordacheckers.cordaclient.dao.GameBoard;
|
||||
|
||||
public record GameBoardMoveRes (GameBoard successStatus, String failureStatus) {
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package djmil.cordacheckers.cordaclient.dao.flow.arguments;
|
||||
|
||||
public record GameProposalActionReq(String gameProposalUuid, Action action) {
|
||||
public enum Action {
|
||||
public record GameProposalActionReq(String gameProposalUuid, Command command) {
|
||||
public enum Command {
|
||||
ACCEPT,
|
||||
REJECT,
|
||||
CANCEL
|
||||
|
@ -0,0 +1,45 @@
|
||||
package djmil.cordacheckers.contracts;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||
import net.corda.v5.ledger.utxo.Command;
|
||||
|
||||
public class GameBoardCommand implements Command {
|
||||
public static enum Type {
|
||||
SURRENDER,
|
||||
DRAW,
|
||||
VICTORY,
|
||||
MOVE;
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final List<Integer> move; // [0] from, [1] to
|
||||
|
||||
public GameBoardCommand(Type type) {
|
||||
if (type == Type.MOVE)
|
||||
throw new CordaRuntimeException (BAD_ACTIONMOVE_CONSTRUCTOR);
|
||||
|
||||
this.type = type;
|
||||
this.move = null;
|
||||
}
|
||||
|
||||
public GameBoardCommand(List<Integer> move) {
|
||||
this.type = Type.MOVE;
|
||||
this.move = move;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public List<Integer> getMove() {
|
||||
if (type != Type.MOVE)
|
||||
throw new CordaRuntimeException (NO_MOVES_FOR_ACTIONTYPE +type);
|
||||
|
||||
return this.move;
|
||||
}
|
||||
|
||||
static final String BAD_ACTIONMOVE_CONSTRUCTOR = "Bad constructor for Action.MOVE";
|
||||
static final String NO_MOVES_FOR_ACTIONTYPE = ".getMove() not possible for ";
|
||||
}
|
@ -11,23 +11,35 @@ 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);
|
||||
final List<Command> commandList = trx.getCommands();
|
||||
requireThat(commandList.size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||
|
||||
final Command command = commandList.get(0);
|
||||
|
||||
if (command instanceof GameProposalCommand) {
|
||||
log.info("GameBoardContract.verify() as GameProposalCommand "+(GameProposalCommand)command);
|
||||
} else
|
||||
if (command instanceof GameBoardCommand) {
|
||||
log.info("GameBoardContract.verify() as GameBoardCommand "+((GameBoardCommand)command).getType());
|
||||
switch (((GameBoardCommand)command).getType()) {
|
||||
case SURRENDER:
|
||||
break;
|
||||
case DRAW:
|
||||
break;
|
||||
case VICTORY:
|
||||
break;
|
||||
case MOVE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
@ -36,20 +48,17 @@ public class GameBoardContract implements net.corda.v5.ledger.utxo.Contract {
|
||||
requireThat(commandList.size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||
|
||||
final Command command = commandList.get(0);
|
||||
|
||||
if (command instanceof Action && (Action)command == Action.ACCEPT) {
|
||||
if (command instanceof GameProposalCommand && (GameProposalCommand)command == GameProposalCommand.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);
|
||||
}
|
||||
if (command instanceof GameBoardCommand) {
|
||||
return trx.getReferenceStates(GameProposalState.class)
|
||||
.stream()
|
||||
.reduce( (a, b) -> {throw new IllegalStateException(SINGLE_STATE_EXPECTED);} )
|
||||
.get();
|
||||
}
|
||||
|
||||
throw new IllegalStateException(NO_REFERANCE_GAMEPROPOSAL_STATE_FOR_TRXID +trx.getId());
|
||||
|
@ -0,0 +1,49 @@
|
||||
package djmil.cordacheckers.contracts;
|
||||
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import net.corda.v5.ledger.utxo.Command;
|
||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||
|
||||
public enum GameProposalCommand implements Command {
|
||||
CREATE,
|
||||
ACCEPT,
|
||||
REJECT,
|
||||
CANCEL;
|
||||
|
||||
public MemberX500Name getInitiator(GameProposalState gameProposalState) {
|
||||
switch (this) {
|
||||
case CREATE:
|
||||
case CANCEL:
|
||||
return gameProposalState.getIssuer();
|
||||
case ACCEPT:
|
||||
case REJECT:
|
||||
return gameProposalState.getAcquier();
|
||||
default:
|
||||
throw new RuntimeException(UNSUPPORTED_VALUE_OF + this.name());
|
||||
}
|
||||
}
|
||||
|
||||
public MemberX500Name getRespondent(GameProposalState gameProposalState) {
|
||||
switch (this) {
|
||||
case CREATE:
|
||||
case CANCEL:
|
||||
return gameProposalState.getAcquier();
|
||||
case ACCEPT:
|
||||
case REJECT:
|
||||
return gameProposalState.getIssuer();
|
||||
default:
|
||||
throw new RuntimeException(UNSUPPORTED_VALUE_OF + this.name());
|
||||
}
|
||||
}
|
||||
|
||||
public MemberX500Name getInitiator(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||
return getInitiator(utxoGameProposal.getState().getContractState());
|
||||
}
|
||||
|
||||
public MemberX500Name getRespondent(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||
return getRespondent(utxoGameProposal.getState().getContractState());
|
||||
}
|
||||
|
||||
public static final String UNSUPPORTED_VALUE_OF = "Unsupported GameProposalCommand value: ";
|
||||
}
|
@ -6,65 +6,19 @@ import org.slf4j.LoggerFactory;
|
||||
import djmil.cordacheckers.states.GameBoardState;
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
import net.corda.v5.ledger.utxo.Command;
|
||||
import net.corda.v5.ledger.utxo.StateAndRef;
|
||||
import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||
|
||||
public class GameProposalContract implements net.corda.v5.ledger.utxo.Contract {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(GameProposalContract.class);
|
||||
|
||||
public static enum Action implements Command {
|
||||
CREATE,
|
||||
ACCEPT,
|
||||
REJECT,
|
||||
CANCEL;
|
||||
|
||||
public MemberX500Name getInitiator(GameProposalState gameProposalState) {
|
||||
switch (this) {
|
||||
case CREATE:
|
||||
case CANCEL:
|
||||
return gameProposalState.getIssuer();
|
||||
case ACCEPT:
|
||||
case REJECT:
|
||||
return gameProposalState.getAcquier();
|
||||
default:
|
||||
throw new RuntimeException(UNSUPPORTED_ACTION_VALUE_OF + this.name());
|
||||
}
|
||||
}
|
||||
|
||||
public MemberX500Name getRespondent(GameProposalState gameProposalState) {
|
||||
switch (this) {
|
||||
case CREATE:
|
||||
case CANCEL:
|
||||
return gameProposalState.getAcquier();
|
||||
case ACCEPT:
|
||||
case REJECT:
|
||||
return gameProposalState.getIssuer();
|
||||
default:
|
||||
throw new RuntimeException(UNSUPPORTED_ACTION_VALUE_OF + this.name());
|
||||
}
|
||||
}
|
||||
|
||||
public MemberX500Name getInitiator(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||
return getInitiator(utxoGameProposal.getState().getContractState());
|
||||
}
|
||||
|
||||
public MemberX500Name getRespondent(StateAndRef<GameProposalState> utxoGameProposal) {
|
||||
return getRespondent(utxoGameProposal.getState().getContractState());
|
||||
}
|
||||
|
||||
public static final String UNSUPPORTED_ACTION_VALUE_OF = "Unsupported Action value: ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(UtxoLedgerTransaction trx) {
|
||||
log.info("GameProposalContract.verify() called");
|
||||
|
||||
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||
|
||||
switch ((trx.getCommands(Action.class).get(0))) {
|
||||
switch ((trx.getCommands(GameProposalCommand.class).get(0))) {
|
||||
case CREATE: {
|
||||
requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE);
|
||||
requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE);
|
||||
|
@ -0,0 +1,97 @@
|
||||
package djmil.cordacheckers.gameboard;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import djmil.cordacheckers.contracts.GameBoardCommand;
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.InitiatedBy;
|
||||
import net.corda.v5.application.flows.ResponderFlow;
|
||||
import net.corda.v5.application.membership.MemberLookup;
|
||||
import net.corda.v5.application.messaging.FlowSession;
|
||||
import net.corda.v5.base.annotations.Suspendable;
|
||||
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||
import net.corda.v5.base.types.MemberX500Name;
|
||||
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.UtxoTransactionValidator;
|
||||
|
||||
@InitiatedBy(protocol = "game-board")
|
||||
public class CommitResponderFlow implements ResponderFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(CommitResponderFlow.class);
|
||||
|
||||
@CordaInject
|
||||
public MemberLookup memberLookup;
|
||||
|
||||
@CordaInject
|
||||
public UtxoLedgerService utxoLedgerService;
|
||||
|
||||
@Suspendable
|
||||
@Override
|
||||
public void call(FlowSession session) {
|
||||
try {
|
||||
UtxoTransactionValidator txValidator = ledgerTransaction -> {
|
||||
final GameBoardCommand command = ledgerTransaction.getCommands(GameBoardCommand.class).get(0);
|
||||
|
||||
// TODO
|
||||
//final GameProposalState gameProposal = getGameProposal(ledgerTransaction);
|
||||
|
||||
//checkParticipants(session, gameProposal, command);
|
||||
|
||||
/*
|
||||
* Other checks / actions ?
|
||||
*/
|
||||
|
||||
log.info("Verified the transaction - " + ledgerTransaction.getId());
|
||||
};
|
||||
|
||||
UtxoSignedTransaction finalizedSignedTransaction = utxoLedgerService
|
||||
.receiveFinality(session, txValidator)
|
||||
.getTransaction();
|
||||
|
||||
log.info("Finished responder flow - " + finalizedSignedTransaction.getId());
|
||||
}
|
||||
catch(Exception e) {
|
||||
log.warn("Responder flow failed: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
void checkParticipants(
|
||||
FlowSession session,
|
||||
GameProposalState gameProposal,
|
||||
GameBoardCommand command
|
||||
) {
|
||||
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||
final MemberX500Name otherName = session.getCounterparty();
|
||||
|
||||
// TODO
|
||||
// if (command.getRespondent(gameProposal).compareTo(myName) != 0)
|
||||
// throw new CordaRuntimeException("Bad GameProposal acquirer: expected '"+myName+"', actual '" +gameProposal.getAcquier() +"'");
|
||||
|
||||
// if (command.getInitiator(gameProposal).compareTo(otherName) != 0)
|
||||
// throw new CordaRuntimeException("Bad GameProposal issuer: expected '"+otherName+"', actual '"+gameProposal.getIssuer()+"'");
|
||||
}
|
||||
|
||||
// @Suspendable
|
||||
// GameProposalState getGameProposal(UtxoLedgerTransaction trx) {
|
||||
// final Action action = trx.getCommands(Action.class).get(0);
|
||||
|
||||
// switch (action) {
|
||||
// case CREATE:
|
||||
// return (GameProposalState)trx.getOutputContractStates().get(0);
|
||||
|
||||
// case ACCEPT:
|
||||
// case REJECT:
|
||||
// case CANCEL:
|
||||
// return (GameProposalState)trx.getInputContractStates().get(0);
|
||||
|
||||
// default:
|
||||
// throw new RuntimeException(Action.UNSUPPORTED_ACTION_VALUE_OF +action);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package djmil.cordacheckers.gameboard;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.InitiatingFlow;
|
||||
import net.corda.v5.application.flows.SubFlow;
|
||||
import net.corda.v5.application.messaging.FlowMessaging;
|
||||
import net.corda.v5.application.messaging.FlowSession;
|
||||
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.UtxoLedgerService;
|
||||
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
||||
|
||||
@InitiatingFlow(protocol = "game-board")
|
||||
public class CommitSubFlow implements SubFlow<SecureHash> {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(CommitSubFlow.class);
|
||||
private final UtxoSignedTransaction signedTransaction;
|
||||
|
||||
public CommitSubFlow(UtxoSignedTransaction signedTransaction) {
|
||||
this.signedTransaction = signedTransaction;
|
||||
}
|
||||
|
||||
@CordaInject
|
||||
public UtxoLedgerService ledgerService;
|
||||
|
||||
@CordaInject
|
||||
public FlowMessaging flowMessaging;
|
||||
|
||||
@Override
|
||||
@Suspendable
|
||||
public SecureHash call() {
|
||||
log.info("GameBoard commit started");
|
||||
|
||||
final MemberX500Name respondenName = null; // TODO
|
||||
|
||||
final FlowSession session = flowMessaging.initiateFlow(respondenName);
|
||||
|
||||
/*
|
||||
* Calls the Corda provided finalise() function which gather signatures from the counterparty,
|
||||
* notarises the transaction and persists the transaction to each party's vault.
|
||||
*/
|
||||
|
||||
final List<FlowSession> sessionsList = Arrays.asList(session);
|
||||
|
||||
final SecureHash trxId = ledgerService
|
||||
.finalize(this.signedTransaction, sessionsList)
|
||||
.getTransaction()
|
||||
.getId();
|
||||
|
||||
log.info("GameBoard commit " +trxId);
|
||||
return trxId;
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package djmil.cordacheckers.gameboard;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MoveFlowArgs {
|
||||
private UUID gameBoardUuid;
|
||||
private List<Integer> move;
|
||||
|
||||
// Serialisation service requires a default constructor
|
||||
public MoveFlowArgs() {
|
||||
this.gameBoardUuid = null;
|
||||
this.move = null;
|
||||
}
|
||||
|
||||
public UUID getGameBoardUuid() {
|
||||
return this.gameBoardUuid;
|
||||
}
|
||||
|
||||
public List<Integer> getMove() {
|
||||
return this.move;
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import djmil.cordacheckers.FlowResult;
|
||||
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||
import djmil.cordacheckers.states.GameBoardState;
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import net.corda.v5.application.flows.ClientRequestBody;
|
||||
@ -23,11 +24,9 @@ import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import static djmil.cordacheckers.contracts.GameProposalContract.Action;
|
||||
|
||||
public class ActionFlow implements ClientStartableFlow {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(CreateFlow.class);
|
||||
private final static Logger log = LoggerFactory.getLogger(ActionFlow.class);
|
||||
|
||||
@CordaInject
|
||||
public JsonMarshallingService jsonMarshallingService;
|
||||
@ -43,23 +42,25 @@ public class ActionFlow implements ClientStartableFlow {
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
try {
|
||||
final ActionFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, ActionFlowArgs.class);
|
||||
final Action action = args.getAction();
|
||||
final GameProposalCommand command = args.getCommand();
|
||||
|
||||
System.out.println("Game Proposal command" + command);
|
||||
|
||||
final StateAndRef<GameProposalState> utxoGameProposal = findUnconsumedGameProposalState(args.getGameProposalUuid());
|
||||
|
||||
final UtxoSignedTransaction trx = prepareSignedTransaction(action, utxoGameProposal);
|
||||
final UtxoSignedTransaction trx = prepareSignedTransaction(command, utxoGameProposal);
|
||||
|
||||
final SecureHash trxId = this.flowEngine
|
||||
.subFlow( new CommitSubFlow(trx, action.getRespondent(utxoGameProposal)) );
|
||||
.subFlow( new CommitSubFlow(trx, command.getRespondent(utxoGameProposal)) );
|
||||
|
||||
if (action == Action.ACCEPT) {
|
||||
if (command == GameProposalCommand.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(command+"ED", trxId) // REJECT+ED
|
||||
.toJsonEncodedString(jsonMarshallingService);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -86,17 +87,17 @@ public class ActionFlow implements ClientStartableFlow {
|
||||
|
||||
@Suspendable
|
||||
private UtxoSignedTransaction prepareSignedTransaction(
|
||||
Action action,
|
||||
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(action)
|
||||
.addCommand(command)
|
||||
.addSignatories(utxoGameProposal.getState().getContractState().getParticipants());
|
||||
|
||||
if (action == Action.ACCEPT) {
|
||||
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
|
||||
|
@ -2,20 +2,20 @@ package djmil.cordacheckers.gameproposal;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import djmil.cordacheckers.contracts.GameProposalContract;
|
||||
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||
|
||||
public class ActionFlowArgs {
|
||||
private UUID gameProposalUuid;
|
||||
private String action;
|
||||
private String command;
|
||||
|
||||
// Serialisation service requires a default constructor
|
||||
public ActionFlowArgs() {
|
||||
this.gameProposalUuid = null;
|
||||
this.action = null;
|
||||
this.command = null;
|
||||
}
|
||||
|
||||
public GameProposalContract.Action getAction() {
|
||||
return GameProposalContract.Action.valueOf(this.action);
|
||||
public GameProposalCommand getCommand() {
|
||||
return GameProposalCommand.valueOf(this.command);
|
||||
}
|
||||
|
||||
public UUID getGameProposalUuid() {
|
||||
|
@ -3,6 +3,7 @@ package djmil.cordacheckers.gameproposal;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import net.corda.v5.application.flows.CordaInject;
|
||||
import net.corda.v5.application.flows.InitiatedBy;
|
||||
@ -17,8 +18,6 @@ import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction;
|
||||
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
||||
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionValidator;
|
||||
|
||||
import static djmil.cordacheckers.contracts.GameProposalContract.Action;
|
||||
|
||||
@InitiatedBy(protocol = "game-proposal")
|
||||
public class CommitResponderFlow implements ResponderFlow {
|
||||
|
||||
@ -35,11 +34,11 @@ public class CommitResponderFlow implements ResponderFlow {
|
||||
public void call(FlowSession session) {
|
||||
try {
|
||||
UtxoTransactionValidator txValidator = ledgerTransaction -> {
|
||||
final Action action = ledgerTransaction.getCommands(Action.class).get(0);
|
||||
final GameProposalCommand command = ledgerTransaction.getCommands(GameProposalCommand.class).get(0);
|
||||
|
||||
final GameProposalState gameProposal = getGameProposal(ledgerTransaction);
|
||||
|
||||
checkParticipants(session, gameProposal, action);
|
||||
checkParticipants(session, gameProposal, command);
|
||||
|
||||
/*
|
||||
* Other checks / actions ?
|
||||
@ -63,23 +62,23 @@ public class CommitResponderFlow implements ResponderFlow {
|
||||
void checkParticipants(
|
||||
FlowSession session,
|
||||
GameProposalState gameProposal,
|
||||
Action action
|
||||
GameProposalCommand command
|
||||
) {
|
||||
final MemberX500Name myName = memberLookup.myInfo().getName();
|
||||
final MemberX500Name otherName = session.getCounterparty();
|
||||
|
||||
if (action.getRespondent(gameProposal).compareTo(myName) != 0)
|
||||
if (command.getRespondent(gameProposal).compareTo(myName) != 0)
|
||||
throw new CordaRuntimeException("Bad GameProposal acquirer: expected '"+myName+"', actual '" +gameProposal.getAcquier() +"'");
|
||||
|
||||
if (action.getInitiator(gameProposal).compareTo(otherName) != 0)
|
||||
if (command.getInitiator(gameProposal).compareTo(otherName) != 0)
|
||||
throw new CordaRuntimeException("Bad GameProposal issuer: expected '"+otherName+"', actual '"+gameProposal.getIssuer()+"'");
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
GameProposalState getGameProposal(UtxoLedgerTransaction trx) {
|
||||
final Action action = trx.getCommands(Action.class).get(0);
|
||||
final GameProposalCommand command = trx.getCommands(GameProposalCommand.class).get(0);
|
||||
|
||||
switch (action) {
|
||||
switch (command) {
|
||||
case CREATE:
|
||||
return (GameProposalState)trx.getOutputContractStates().get(0);
|
||||
|
||||
@ -89,7 +88,7 @@ public class CommitResponderFlow implements ResponderFlow {
|
||||
return (GameProposalState)trx.getInputContractStates().get(0);
|
||||
|
||||
default:
|
||||
throw new RuntimeException(Action.UNSUPPORTED_ACTION_VALUE_OF +action);
|
||||
throw new RuntimeException(GameProposalCommand.UNSUPPORTED_VALUE_OF +command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import djmil.cordacheckers.FlowResult;
|
||||
import djmil.cordacheckers.contracts.GameProposalCommand;
|
||||
import djmil.cordacheckers.states.GameProposalState;
|
||||
import djmil.cordacheckers.states.Piece;
|
||||
import net.corda.v5.application.flows.ClientRequestBody;
|
||||
@ -22,7 +23,6 @@ import net.corda.v5.membership.MemberInfo;
|
||||
import net.corda.v5.membership.NotaryInfo;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static djmil.cordacheckers.contracts.GameProposalContract.Action;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@ -53,14 +53,14 @@ public class CreateFlow implements ClientStartableFlow{
|
||||
public String call(ClientRequestBody requestBody) {
|
||||
try {
|
||||
log.info("flow: Create Game Proposal");
|
||||
final Action actino = Action.CREATE;
|
||||
final GameProposalCommand command = GameProposalCommand.CREATE;
|
||||
|
||||
final GameProposalState newGameProposal = buildGameProposalStateFrom(requestBody);
|
||||
|
||||
final UtxoSignedTransaction trx = prepareSignedTransaction(newGameProposal);
|
||||
|
||||
final SecureHash trxId = this.flowEngine
|
||||
.subFlow(new CommitSubFlow(trx, actino.getRespondent(newGameProposal)));
|
||||
.subFlow(new CommitSubFlow(trx, command.getRespondent(newGameProposal)));
|
||||
|
||||
return new FlowResult(newGameProposal.getId(), trxId)
|
||||
.toJsonEncodedString(jsonMarshallingService);
|
||||
@ -107,7 +107,7 @@ public class CreateFlow implements ClientStartableFlow{
|
||||
.setNotary(notary.getName())
|
||||
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||
.addOutputState(outputGameProposalState)
|
||||
.addCommand(Action.CREATE)
|
||||
.addCommand(GameProposalCommand.CREATE)
|
||||
.addSignatories(outputGameProposalState.getParticipants())
|
||||
.toSignedTransaction();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user