diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/GameProposal.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/GameProposal.java index 48b6c5a..c742f19 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/GameProposal.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/GameProposal.java @@ -6,9 +6,9 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize public record GameProposal( - String sender, - String recipient, - Color recipientColor, + String issuer, + String acquier, + Color acquierColor, String message, UUID id) { diff --git a/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java b/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java index 00fae11..d5fe9d8 100644 --- a/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java +++ b/backend/src/test/java/djmil/cordacheckers/cordaclient/CordaClientTest.java @@ -47,27 +47,33 @@ public class CordaClientTest { @Test void testGemeProposalCreate() throws JsonMappingException, JsonProcessingException { - final String gpSender = "alice"; - final String gpReceiver = "bob"; - final Color gpReceiverColor = Color.WHITE; + final String gpIssuer = "alice"; + final String gpAcquier = "bob"; + final Color gpAcquierColor = Color.WHITE; final String gpMessage = "GameProposal create test"; final UUID createdGpUuid = cordaClient.gameProposalCreate( - holdingIdentityResolver.getByUsername(gpSender), - holdingIdentityResolver.getByUsername(gpReceiver), - gpReceiverColor, + holdingIdentityResolver.getByUsername(gpIssuer), + holdingIdentityResolver.getByUsername(gpAcquier), + gpAcquierColor, gpMessage ); List gpListSender = cordaClient.gameProposalList( - holdingIdentityResolver.getByUsername(gpSender)); + holdingIdentityResolver.getByUsername(gpIssuer)); assertThat(findByUuid(gpListSender, createdGpUuid)).isNotNull(); List gpListReceiver = cordaClient.gameProposalList( - holdingIdentityResolver.getByUsername(gpReceiver)); + holdingIdentityResolver.getByUsername(gpAcquier)); - assertThat(findByUuid(gpListReceiver, createdGpUuid)).isNotNull(); + GameProposal gp; + assertThat(gp = findByUuid(gpListReceiver, createdGpUuid)).isNotNull(); + + assertThat(gp.acquier()).isEqualToIgnoringCase(gpAcquier); + assertThat(gp.issuer()).isEqualToIgnoringCase(gpIssuer); + assertThat(gp.acquierColor()).isEqualByComparingTo(gpAcquierColor); + assertThat(gp.message()).isEqualTo(gpMessage); } @Test diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalResolutionState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalResolutionState.java deleted file mode 100644 index 96573e6..0000000 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalResolutionState.java +++ /dev/null @@ -1,63 +0,0 @@ -package djmil.cordacheckers.states; - -import java.security.PublicKey; -import java.util.List; - -import djmil.cordacheckers.contracts.GameProposalContract; -import net.corda.v5.base.annotations.ConstructorForDeserialization; -import net.corda.v5.base.annotations.CordaSerializable; -import net.corda.v5.base.types.MemberX500Name; -import net.corda.v5.ledger.utxo.BelongsToContract; -import net.corda.v5.ledger.utxo.ContractState; - -@BelongsToContract(GameProposalContract.class) -public class GameProposalResolutionState implements ContractState { - - @CordaSerializable - public enum Resolution { - ACCEPT, - REJECT, - CANCEL - } - - public final Resolution outcome; - public final List participants; - - public GameProposalResolutionState( - Resolution outcome, - GameProposalState gameProposal - ) { - this.outcome = outcome; - this.participants = gameProposal.getParticipants(); - } - - @ConstructorForDeserialization - public GameProposalResolutionState( - Resolution outcome, - List participants - ) { - this.outcome = outcome; - this.participants = participants; - } - - public Resolution getOutcome() { - return outcome; - } - - public List getParticipants() { - return this.participants; - } - - public MemberX500Name getRecipient(GameProposalState gameProposal) { - switch (outcome) { - case ACCEPT: - case REJECT: - return gameProposal.getSender(); - case CANCEL: - return gameProposal.getRecipient(); - default: - throw new RuntimeException("Unknown Resolution value: "+outcome.toString()); - } - } - -} diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java index cafd9c6..764976b 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java @@ -13,8 +13,8 @@ import net.corda.v5.ledger.utxo.ContractState; @BelongsToContract(GameProposalContract.class) public class GameProposalState implements ContractState { - MemberX500Name sender; - MemberX500Name recipient; + MemberX500Name issuer; + MemberX500Name acquier; Piece.Color recipientColor; String message; UUID id; @@ -23,27 +23,27 @@ public class GameProposalState implements ContractState { // Allows serialisation and to use a specified UUID @ConstructorForDeserialization public GameProposalState( - MemberX500Name sender, - MemberX500Name recipient, + MemberX500Name issuer, + MemberX500Name acquier, Piece.Color recipientColor, String message, UUID id, List participants ) { - this.sender = sender; - this.recipient = recipient; + this.issuer = issuer; + this.acquier = acquier; this.recipientColor = recipientColor; this.message = message; this.id = id; this.participants = participants; } - public MemberX500Name getSender() { - return sender; + public MemberX500Name getIssuer() { + return issuer; } - public MemberX500Name getRecipient() { - return recipient; + public MemberX500Name getAcquier() { + return acquier; } public Piece.Color getRecipientColor() { diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlow.java index 1d1e237..93f6290 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlow.java @@ -2,7 +2,6 @@ package djmil.cordacheckers.gameproposal; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; @@ -11,7 +10,6 @@ import org.slf4j.LoggerFactory; import djmil.cordacheckers.FlowResult; import djmil.cordacheckers.contracts.GameProposalContract; -import djmil.cordacheckers.states.GameProposalResolutionState; import djmil.cordacheckers.states.GameProposalState; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; @@ -23,8 +21,6 @@ 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.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.UtxoLedgerService; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; @@ -50,12 +46,6 @@ public class ActionFlow implements ClientStartableFlow { @CordaInject public MemberLookup memberLookup; - private final static Map resoultion2command = Map.ofEntries( - Map.entry(GameProposalResolutionState.Resolution.CANCEL, new GameProposalContract.Cancel()), - Map.entry(GameProposalResolutionState.Resolution.REJECT, new GameProposalContract.Reject()), - Map.entry(GameProposalResolutionState.Resolution.ACCEPT, new GameProposalContract.Accept()) - ); - @Override @Suspendable public String call(ClientRequestBody requestBody) { @@ -63,13 +53,8 @@ public class ActionFlow implements ClientStartableFlow { ActionFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, ActionFlowArgs.class); StateAndRef inputState = findUnconsumedGameProposalState(args.getGameProposalUuid()); - - GameProposalResolutionState outputState = new GameProposalResolutionState( - args.getAction(), - inputState.getState().getContractState() - ); - String trxResult = doTrunsaction(inputState, outputState); + String trxResult = doTrunsaction(args.getAction(), inputState /*, outputState*/); return new FlowResult(trxResult).toJsonEncodedString(jsonMarshallingService); } @@ -83,7 +68,7 @@ public class ActionFlow implements ClientStartableFlow { @Suspendable private StateAndRef findUnconsumedGameProposalState (UUID gameProposalUuid) { /* - * Get list of all unconsumed aka 'actuve' GameProposalStates, then filter by UUID. + * Get list of all unconsumed aka 'active' GameProposalStates, then filter by UUID. * Note, this is an inefficient way to perform this operation if there are a large * number of 'active' GameProposals exists in storage. */ @@ -102,26 +87,30 @@ public class ActionFlow implements ClientStartableFlow { } @Suspendable - private String doTrunsaction(StateAndRef inputState, GameProposalResolutionState outputState) { + private String doTrunsaction( + GameProposalContract.Action action, + StateAndRef inputState + /* GameProposalResolutionState outputState*/ + ) { UtxoTransactionBuilder txBuilder = ledgerService.createTransactionBuilder() .setNotary(inputState.getState().getNotaryName()) .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .addInputState(inputState.getRef()) - .addOutputState(outputState) - .addCommand(resoultion2command.get(outputState.outcome)) - .addSignatories(outputState.getParticipants()); + //.addOutputState(outputState) + .addCommand(action) + .addSignatories(inputState.getState().getContractState().getParticipants()); UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(); FlowSession session = flowMessaging.initiateFlow( - outputState.getRecipient(inputState.getState().getContractState()) + action.getReceiver(inputState) ); List sessionsList = Arrays.asList(session); ledgerService.finalize(signedTransaction, sessionsList); - return outputState.getOutcome()+"ED"; // REJECT+ED + return action+"ED"; // REJECT+ED } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlowArgs.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlowArgs.java index 38cd8e7..ae9f455 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlowArgs.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionFlowArgs.java @@ -2,8 +2,7 @@ package djmil.cordacheckers.gameproposal; import java.util.UUID; -import djmil.cordacheckers.states.GameProposalResolutionState; -import djmil.cordacheckers.states.GameProposalResolutionState.Resolution; +import djmil.cordacheckers.contracts.GameProposalContract; public class ActionFlowArgs { private UUID gameProposalUuid; @@ -15,8 +14,8 @@ public class ActionFlowArgs { this.action = null; } - public Resolution getAction() { - return GameProposalResolutionState.Resolution.valueOf(this.action); + public GameProposalContract.Action getAction() { + return GameProposalContract.Action.valueOf(this.action); } public UUID getGameProposalUuid() { diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionResponder.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionResponder.java index c5fc06e..6047d79 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionResponder.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ActionResponder.java @@ -3,9 +3,7 @@ package djmil.cordacheckers.gameproposal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import djmil.cordacheckers.contracts.GameProposalContract.Accept; -import djmil.cordacheckers.contracts.GameProposalContract.Cancel; -import djmil.cordacheckers.contracts.GameProposalContract.Reject; +import djmil.cordacheckers.contracts.GameProposalContract; import djmil.cordacheckers.states.GameProposalState; import net.corda.v5.application.flows.CordaInject; import net.corda.v5.application.flows.InitiatedBy; @@ -15,7 +13,6 @@ 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.Command; import net.corda.v5.ledger.utxo.UtxoLedgerService; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; import net.corda.v5.ledger.utxo.transaction.UtxoTransactionValidator; @@ -37,25 +34,19 @@ public class ActionResponder implements ResponderFlow { UtxoTransactionValidator txValidator = ledgerTransaction -> { GameProposalState gameProposal = (GameProposalState) ledgerTransaction.getInputContractStates().get(0); - Command command = ledgerTransaction.getCommands(Command.class).get(0); + GameProposalContract.Action action = ledgerTransaction.getCommands(GameProposalContract.Action.class).get(0); - if (!checkParticipants(gameProposal, session.getCounterparty(), command)) { - throw new CordaRuntimeException("Failed verification"); - } + checkSessionParticipants(session, gameProposal, action); log.info("Verified the transaction - " + ledgerTransaction.getId()); }; - // Calls receiveFinality() function which provides the responder to the finalise() function - // in the Initiating Flow. Accepts a lambda validator containing the business logic to decide whether - // responder should sign the Transaction. UtxoSignedTransaction finalizedSignedTransaction = utxoLedgerService .receiveFinality(session, txValidator) .getTransaction(); log.info("Finished responder flow - " + finalizedSignedTransaction.getId()); } - // Soft fails the flow and log the exception. catch(Exception e) { log.warn("Exceptionally finished responder flow", e); @@ -63,21 +54,18 @@ public class ActionResponder implements ResponderFlow { } @Suspendable - Boolean checkParticipants(GameProposalState gameProposal, MemberX500Name counterpartyName, Command command) { - MemberX500Name myName = memberLookup.myInfo().getName(); + void checkSessionParticipants( + FlowSession session, + GameProposalState gameProposal, + GameProposalContract.Action action + ) { + final MemberX500Name myName = memberLookup.myInfo().getName(); + final MemberX500Name otherName = session.getCounterparty(); - if (command instanceof Reject || command instanceof Accept) { - if (gameProposal.getRecipient().compareTo(counterpartyName) == 0 && - gameProposal.getSender().compareTo(myName) == 0) - return true; - } - - if (command instanceof Cancel) { - if (gameProposal.getRecipient().compareTo(myName) == 0 && - gameProposal.getSender().compareTo(counterpartyName) == 0) - return true; - } - - return false; + if (action.getReceiver(gameProposal).compareTo(myName) != 0) + throw new CordaRuntimeException("Bad GameProposal aquirer: expected '"+myName+"', actual '" +gameProposal.getAcquier() +"'"); + + if (action.getSender(gameProposal).compareTo(otherName) != 0) + throw new CordaRuntimeException("Bad GameProposal issuer: expected '"+otherName+"', actual '"+gameProposal.getIssuer()+"'"); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java index a5dd10c..b392990 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java @@ -109,12 +109,12 @@ public class CreateFlow implements ClientStartableFlow{ .setNotary(notary.getName()) .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) .addOutputState(gameProposal) - .addCommand(new GameProposalContract.Create()) + .addCommand(GameProposalContract.Action.CREATE) .addSignatories(gameProposal.getParticipants()); UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(); - FlowSession session = flowMessaging.initiateFlow(gameProposal.getRecipient()); + FlowSession session = flowMessaging.initiateFlow(gameProposal.getAcquier()); List sessionsList = Arrays.asList(session); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateResponder.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateResponder.java index 157dda7..4453295 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateResponder.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateResponder.java @@ -62,8 +62,8 @@ public class CreateResponder implements ResponderFlow { Boolean checkParticipants(GameProposalState gameProposal, MemberX500Name counterpartyName) { MemberX500Name myName = memberLookup.myInfo().getName(); - if (gameProposal.getRecipient().compareTo(myName) == 0 && - gameProposal.getSender().compareTo(counterpartyName) == 0) + if (gameProposal.getAcquier().compareTo(myName) == 0 && + gameProposal.getIssuer().compareTo(counterpartyName) == 0) return true; return false; diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ListItem.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ListItem.java index 5e7c259..9b9482d 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ListItem.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/ListItem.java @@ -10,26 +10,26 @@ import djmil.cordacheckers.states.GameProposalState; // and a UUID. It is possible to create custom serializers for the JsonMarshallingService in the future. public class ListItem { - public final String sender; - public final String recipient; - public final String recipientColor; + public final String issuer; + public final String acquier; + public final String acquierColor; public final String message; public final UUID id; // Serialisation service requires a default constructor public ListItem() { - this.sender = null; - this.recipient = null; - this.recipientColor = null; + this.issuer = null; + this.acquier = null; + this.acquierColor = null; this.message = null; this.id = null; } public ListItem(GameProposalState state) { - this.sender = state.getSender().getCommonName(); - this.recipient = state.getRecipient().getCommonName(); - this.recipientColor = state.getRecipientColor().name(); - this.message = state.getMessage(); - this.id = state.getId(); + this.issuer = state.getIssuer().getCommonName(); + this.acquier = state.getAcquier().getCommonName(); + this.acquierColor = state.getRecipientColor().name(); + this.message = state.getMessage(); + this.id = state.getId(); } }