diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java index 64785ee..86fc615 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java @@ -25,8 +25,8 @@ public class GameResultState extends GameState { this.totalMoves = totalMoves; } - public GameResultState(MemberX500Name winnerName, GameBoardState gameBoardState, PublicKey custodianPubicKey) { - super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants, custodianPubicKey); + public GameResultState(MemberX500Name winnerName, GameBoardState gameBoardState) { + super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants); this.winnerName = winnerName; this.totalMoves = gameBoardState.getMoveNumber(); } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java index 98f8537..9b3beb8 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java @@ -1,7 +1,6 @@ package djmil.cordacheckers.states; import java.security.PublicKey; -import java.util.LinkedList; import java.util.List; import java.util.UUID; @@ -27,18 +26,6 @@ public abstract class GameState implements ContractState { this.participants = participants; } - GameState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, UUID gameUuid, - String message, List participants, PublicKey additionalParticipand) { - this.whitePlayer = whitePlayer; - this.blackPlayer = blackPlayer; - this.gameUuid = gameUuid; - this.message = message; - - var deepCopy = new LinkedList<>(participants); - deepCopy.add(additionalParticipand); - this.participants = deepCopy; - } - public MemberX500Name getWhitePlayer() { return whitePlayer; } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRejectFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRejectFlow.java index e77323b..85cb9a3 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRejectFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRejectFlow.java @@ -66,7 +66,7 @@ public class DrawRejectFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(drawDeclineTrx, currenGameBoardState.getActivePlayerName())); + .subFlow(new CommitTrx(drawDeclineTrx, currenGameBoardState.getParticipants())); final View gameStateView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRequestFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRequestFlow.java index f7866a8..10fd7d1 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRequestFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/DrawRequestFlow.java @@ -70,7 +70,7 @@ public class DrawRequestFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(drawReqTrx, currenGameBoardState.getIdelPlayerName())); + .subFlow(new CommitTrx(drawReqTrx, currenGameBoardState.getParticipants())); final View gameStateView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/MoveFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/MoveFlow.java index 835415d..ebe1139 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/MoveFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/MoveFlow.java @@ -74,7 +74,7 @@ public class MoveFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(moveTrx, currenGameBoardState.getIdelPlayerName())); + .subFlow(new CommitTrx(moveTrx, currenGameBoardState.getParticipants())); if (amIwon(newGameBoard)) { log.info("Opponent has no possible moves. Claim victory!"); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java index 6914f54..00f6629 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/AcceptFlow.java @@ -66,7 +66,7 @@ public class AcceptFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(gameProposalAcceptTrx, gameProposal.getIssuerName())); + .subFlow(new CommitTrx(gameProposalAcceptTrx, gameProposal.getParticipants())); final View gameView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java index 07b2164..2078496 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CancelFlow.java @@ -62,7 +62,7 @@ public class CancelFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(gameProposalCancelTrx, gameProposal.getAcquierName())); + .subFlow(new CommitTrx(gameProposalCancelTrx, gameProposal.getParticipants())); final View gameStateView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); 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 807f66b..3886523 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CreateFlow.java @@ -59,8 +59,9 @@ public class CreateFlow implements ClientStartableFlow{ try { final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CREATE); + final MemberInfo custodianInfo = findCustodian(); - final GameProposalState gameProposal = buildGameProposalStateFrom(requestBody); + final GameProposalState gameProposal = buildGameProposalStateFrom(requestBody, custodianInfo); final UtxoSignedTransaction gameProposalCreateTrx = utxoLedgerService.createTransactionBuilder() .addCommand(command) @@ -71,7 +72,7 @@ public class CreateFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(gameProposalCreateTrx, gameProposal.getAcquierName())); + .subFlow(new CommitTrx(gameProposalCreateTrx, gameProposal.getParticipants())); final View gameView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); @@ -87,7 +88,7 @@ public class CreateFlow implements ClientStartableFlow{ } @Suspendable - GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody) { + GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody, MemberInfo custodianInfo) { final CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class); final MemberInfo myInfo = memberLookup.myInfo(); @@ -108,7 +109,9 @@ public class CreateFlow implements ClientStartableFlow{ args.message, Arrays.asList( myInfo.getLedgerKeys().get(0), - opponentInfo.getLedgerKeys().get(0)) + opponentInfo.getLedgerKeys().get(0), + custodianInfo.getLedgerKeys().get(0) + ) ); } @@ -121,4 +124,12 @@ public class CreateFlow implements ClientStartableFlow{ .orElseThrow( () -> new IllegalStateException("No Notary VNode found")); } + @Suspendable + MemberInfo findCustodian() { + return memberLookup.lookup() + .stream() + .filter(member -> VNode.isCordaCherckersCustodian(member) ) + .reduce((a,b) -> {throw new IllegalStateException("Multiple Custodian VNodes");}) + .orElseThrow( () -> new IllegalStateException("No Custodian VNode found")); + } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java index 2ef6c38..7352903 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/RejectFlow.java @@ -70,7 +70,7 @@ public class RejectFlow implements ClientStartableFlow{ .toSignedTransaction(); utxoTrxId = this.flowEngine - .subFlow(new CommitTrx(gameProposalRejectTrx, gameProposal.getIssuerName())); + .subFlow(new CommitTrx(gameProposalRejectTrx, gameProposal.getParticipants())); final View gameStateView = this.flowEngine .subFlow(new ViewBuilder(utxoTrxId)); diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultCommiter.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultCommiter.java index 2c964ce..7a24719 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultCommiter.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultCommiter.java @@ -1,6 +1,5 @@ package djmil.cordacheckers.gameresult; -import java.security.PublicKey; import java.time.Duration; import java.time.Instant; import java.util.UUID; @@ -46,13 +45,12 @@ public class GameResultCommiter implements SubFlow { @Override @Suspendable public SecureHash call() { - final MemberInfo custodianInfo = findCustodian(); final StateAndRef gameBoardSar = this.flowEngine .subFlow(new GetFlow(this.gameUuid)); final GameBoardState gameBoard = (GameBoardState)gameBoardSar.getState().getContractState(); - final GameResultState gameResult = gameResultBuilder(gameBoard, custodianInfo); + final GameResultState gameResult = gameResultBuilder(gameBoard); final UtxoSignedTransaction gameResultTrx = utxoLedgerService.createTransactionBuilder() .addCommand(this.command) @@ -64,9 +62,7 @@ public class GameResultCommiter implements SubFlow { .toSignedTransaction(); return this.flowEngine.subFlow( - new CommitTrx(gameResultTrx, - getCounterparty(gameResult), - custodianInfo.getName()) ); + new CommitTrx(gameResultTrx, gameResult.getParticipants())); } @Suspendable @@ -79,32 +75,25 @@ public class GameResultCommiter implements SubFlow { } @Suspendable - GameResultState gameResultBuilder(GameBoardState gameBoard, MemberInfo custodiaInfo) { - final PublicKey custodianPublicKey = custodiaInfo.getLedgerKeys().get(0); + GameResultState gameResultBuilder(GameBoardState gameBoard) { final MemberX500Name myName = memberLookup.myInfo().getName(); switch(this.command.getAction()) { case CLAIM_VICTORY: return new GameResultState(myName, // i'm a winner - gameBoard, custodianPublicKey); + gameBoard); case SURRENDER: return new GameResultState(gameBoard.getOpponentName(myName), // me surrender to opponent - gameBoard, custodianPublicKey); + gameBoard); case DRAW_ACCEPT: return new GameResultState(null, // there is no winner, it's a draw - gameBoard, custodianPublicKey); + gameBoard); default: throw new IllegalStateException("GameResult: bad reason"); } } - @Suspendable - MemberX500Name getCounterparty(GameState gameState) { - final MemberX500Name myName = this.memberLookup.myInfo().getName(); - return gameState.getOpponentName(myName); - } - } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitTrx.java b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitTrx.java index 7141c5e..4ad5a05 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitTrx.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gamestate/CommitTrx.java @@ -1,5 +1,6 @@ package djmil.cordacheckers.gamestate; +import java.security.PublicKey; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -19,24 +20,19 @@ import net.corda.v5.crypto.SecureHash; import net.corda.v5.ledger.utxo.UtxoLedgerService; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; +import static java.util.Objects.*; +import static java.util.stream.Collectors.toList; + @InitiatingFlow(protocol = "gamestate-commit") public class CommitTrx implements SubFlow { private final static Logger log = LoggerFactory.getLogger(CommitTrx.class); private final UtxoSignedTransaction utxTrxCandidate; - private final MemberX500Name counterpartyName; - private final MemberX500Name custodyName; + private final List participants; - public CommitTrx(UtxoSignedTransaction signedTransaction, MemberX500Name counterpartyName) { + public CommitTrx(UtxoSignedTransaction signedTransaction, List participants) { this.utxTrxCandidate = signedTransaction; - this.counterpartyName = counterpartyName; - this.custodyName = null; - } - - public CommitTrx(UtxoSignedTransaction signedTransaction, MemberX500Name counterpartyName, MemberX500Name custodyName) { - this.utxTrxCandidate = signedTransaction; - this.counterpartyName = counterpartyName; - this.custodyName = custodyName; + this.participants = participants; } @CordaInject @@ -53,24 +49,29 @@ public class CommitTrx implements SubFlow { public SecureHash call() { log.info("GameState commit started"); + final MemberX500Name myName = memberLookup.myInfo().getName(); + + List sessions = participants.stream() + .map(pubKey -> requireNonNull(memberLookup.lookup(pubKey), "Member not found from public Key " + pubKey + ".").getName()) + .filter(person -> person.compareTo(myName) != 0) + .map(signatorieX500name -> flowMessaging.initiateFlow(signatorieX500name)) + .collect(toList()); + + if (sessions.size() != 2) + throw new RuntimeException("Should be strictly TWO signatories other than the initiator"); + /* - * Calls the Corda provided finalise() function which gather signatures from the counterparty, + * 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 FlowSession session = flowMessaging.initiateFlow(this.counterpartyName); - List sessionsList = new LinkedList(Arrays.asList(session)); - - if (custodyName != null) { - sessionsList.add(flowMessaging.initiateFlow(custodyName)); - } - final SecureHash trxId = ledgerService - .finalize(this.utxTrxCandidate, sessionsList) - .getTransaction() - .getId(); + .finalize(this.utxTrxCandidate, sessions) + .getTransaction() + .getId(); - log.info("GameState commit " +trxId); + log.info("GameState commit " + trxId); return trxId; } }