This commit is contained in:
djmil 2023-11-29 12:10:13 +01:00
parent b6b1358131
commit dce7f8e549
11 changed files with 53 additions and 65 deletions

View File

@ -25,8 +25,8 @@ public class GameResultState extends GameState {
this.totalMoves = totalMoves; this.totalMoves = totalMoves;
} }
public GameResultState(MemberX500Name winnerName, GameBoardState gameBoardState, PublicKey custodianPubicKey) { public GameResultState(MemberX500Name winnerName, GameBoardState gameBoardState) {
super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants, custodianPubicKey); super(gameBoardState.whitePlayer, gameBoardState.blackPlayer, gameBoardState.gameUuid, null, gameBoardState.participants);
this.winnerName = winnerName; this.winnerName = winnerName;
this.totalMoves = gameBoardState.getMoveNumber(); this.totalMoves = gameBoardState.getMoveNumber();
} }

View File

@ -1,7 +1,6 @@
package djmil.cordacheckers.states; package djmil.cordacheckers.states;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -27,18 +26,6 @@ public abstract class GameState implements ContractState {
this.participants = participants; this.participants = participants;
} }
GameState(MemberX500Name whitePlayer, MemberX500Name blackPlayer, UUID gameUuid,
String message, List<PublicKey> 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() { public MemberX500Name getWhitePlayer() {
return whitePlayer; return whitePlayer;
} }

View File

@ -66,7 +66,7 @@ public class DrawRejectFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(drawDeclineTrx, currenGameBoardState.getActivePlayerName())); .subFlow(new CommitTrx(drawDeclineTrx, currenGameBoardState.getParticipants()));
final View gameStateView = this.flowEngine final View gameStateView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));

View File

@ -70,7 +70,7 @@ public class DrawRequestFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(drawReqTrx, currenGameBoardState.getIdelPlayerName())); .subFlow(new CommitTrx(drawReqTrx, currenGameBoardState.getParticipants()));
final View gameStateView = this.flowEngine final View gameStateView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));

View File

@ -74,7 +74,7 @@ public class MoveFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(moveTrx, currenGameBoardState.getIdelPlayerName())); .subFlow(new CommitTrx(moveTrx, currenGameBoardState.getParticipants()));
if (amIwon(newGameBoard)) { if (amIwon(newGameBoard)) {
log.info("Opponent has no possible moves. Claim victory!"); log.info("Opponent has no possible moves. Claim victory!");

View File

@ -66,7 +66,7 @@ public class AcceptFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(gameProposalAcceptTrx, gameProposal.getIssuerName())); .subFlow(new CommitTrx(gameProposalAcceptTrx, gameProposal.getParticipants()));
final View gameView = this.flowEngine final View gameView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));

View File

@ -62,7 +62,7 @@ public class CancelFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(gameProposalCancelTrx, gameProposal.getAcquierName())); .subFlow(new CommitTrx(gameProposalCancelTrx, gameProposal.getParticipants()));
final View gameStateView = this.flowEngine final View gameStateView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));

View File

@ -59,8 +59,9 @@ public class CreateFlow implements ClientStartableFlow{
try { try {
final GameCommand command = new GameCommand(GameCommand.Action.GAME_PROPOSAL_CREATE); 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() final UtxoSignedTransaction gameProposalCreateTrx = utxoLedgerService.createTransactionBuilder()
.addCommand(command) .addCommand(command)
@ -71,7 +72,7 @@ public class CreateFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(gameProposalCreateTrx, gameProposal.getAcquierName())); .subFlow(new CommitTrx(gameProposalCreateTrx, gameProposal.getParticipants()));
final View gameView = this.flowEngine final View gameView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));
@ -87,7 +88,7 @@ public class CreateFlow implements ClientStartableFlow{
} }
@Suspendable @Suspendable
GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody) { GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody, MemberInfo custodianInfo) {
final CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class); final CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class);
final MemberInfo myInfo = memberLookup.myInfo(); final MemberInfo myInfo = memberLookup.myInfo();
@ -108,7 +109,9 @@ public class CreateFlow implements ClientStartableFlow{
args.message, args.message,
Arrays.asList( Arrays.asList(
myInfo.getLedgerKeys().get(0), 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")); .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"));
}
} }

View File

@ -70,7 +70,7 @@ public class RejectFlow implements ClientStartableFlow{
.toSignedTransaction(); .toSignedTransaction();
utxoTrxId = this.flowEngine utxoTrxId = this.flowEngine
.subFlow(new CommitTrx(gameProposalRejectTrx, gameProposal.getIssuerName())); .subFlow(new CommitTrx(gameProposalRejectTrx, gameProposal.getParticipants()));
final View gameStateView = this.flowEngine final View gameStateView = this.flowEngine
.subFlow(new ViewBuilder(utxoTrxId)); .subFlow(new ViewBuilder(utxoTrxId));

View File

@ -1,6 +1,5 @@
package djmil.cordacheckers.gameresult; package djmil.cordacheckers.gameresult;
import java.security.PublicKey;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
@ -46,13 +45,12 @@ public class GameResultCommiter implements SubFlow<SecureHash> {
@Override @Override
@Suspendable @Suspendable
public SecureHash call() { public SecureHash call() {
final MemberInfo custodianInfo = findCustodian();
final StateAndRef<GameState> gameBoardSar = this.flowEngine final StateAndRef<GameState> gameBoardSar = this.flowEngine
.subFlow(new GetFlow(this.gameUuid)); .subFlow(new GetFlow(this.gameUuid));
final GameBoardState gameBoard = (GameBoardState)gameBoardSar.getState().getContractState(); final GameBoardState gameBoard = (GameBoardState)gameBoardSar.getState().getContractState();
final GameResultState gameResult = gameResultBuilder(gameBoard, custodianInfo); final GameResultState gameResult = gameResultBuilder(gameBoard);
final UtxoSignedTransaction gameResultTrx = utxoLedgerService.createTransactionBuilder() final UtxoSignedTransaction gameResultTrx = utxoLedgerService.createTransactionBuilder()
.addCommand(this.command) .addCommand(this.command)
@ -64,9 +62,7 @@ public class GameResultCommiter implements SubFlow<SecureHash> {
.toSignedTransaction(); .toSignedTransaction();
return this.flowEngine.subFlow( return this.flowEngine.subFlow(
new CommitTrx(gameResultTrx, new CommitTrx(gameResultTrx, gameResult.getParticipants()));
getCounterparty(gameResult),
custodianInfo.getName()) );
} }
@Suspendable @Suspendable
@ -79,32 +75,25 @@ public class GameResultCommiter implements SubFlow<SecureHash> {
} }
@Suspendable @Suspendable
GameResultState gameResultBuilder(GameBoardState gameBoard, MemberInfo custodiaInfo) { GameResultState gameResultBuilder(GameBoardState gameBoard) {
final PublicKey custodianPublicKey = custodiaInfo.getLedgerKeys().get(0);
final MemberX500Name myName = memberLookup.myInfo().getName(); final MemberX500Name myName = memberLookup.myInfo().getName();
switch(this.command.getAction()) { switch(this.command.getAction()) {
case CLAIM_VICTORY: case CLAIM_VICTORY:
return new GameResultState(myName, // i'm a winner return new GameResultState(myName, // i'm a winner
gameBoard, custodianPublicKey); gameBoard);
case SURRENDER: case SURRENDER:
return new GameResultState(gameBoard.getOpponentName(myName), // me surrender to opponent return new GameResultState(gameBoard.getOpponentName(myName), // me surrender to opponent
gameBoard, custodianPublicKey); gameBoard);
case DRAW_ACCEPT: case DRAW_ACCEPT:
return new GameResultState(null, // there is no winner, it's a draw return new GameResultState(null, // there is no winner, it's a draw
gameBoard, custodianPublicKey); gameBoard);
default: default:
throw new IllegalStateException("GameResult: bad reason"); throw new IllegalStateException("GameResult: bad reason");
} }
} }
@Suspendable
MemberX500Name getCounterparty(GameState gameState) {
final MemberX500Name myName = this.memberLookup.myInfo().getName();
return gameState.getOpponentName(myName);
}
} }

View File

@ -1,5 +1,6 @@
package djmil.cordacheckers.gamestate; package djmil.cordacheckers.gamestate;
import java.security.PublicKey;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; 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.UtxoLedgerService;
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
import static java.util.Objects.*;
import static java.util.stream.Collectors.toList;
@InitiatingFlow(protocol = "gamestate-commit") @InitiatingFlow(protocol = "gamestate-commit")
public class CommitTrx implements SubFlow<SecureHash> { public class CommitTrx implements SubFlow<SecureHash> {
private final static Logger log = LoggerFactory.getLogger(CommitTrx.class); private final static Logger log = LoggerFactory.getLogger(CommitTrx.class);
private final UtxoSignedTransaction utxTrxCandidate; private final UtxoSignedTransaction utxTrxCandidate;
private final MemberX500Name counterpartyName; private final List<PublicKey> participants;
private final MemberX500Name custodyName;
public CommitTrx(UtxoSignedTransaction signedTransaction, MemberX500Name counterpartyName) { public CommitTrx(UtxoSignedTransaction signedTransaction, List<PublicKey> participants) {
this.utxTrxCandidate = signedTransaction; this.utxTrxCandidate = signedTransaction;
this.counterpartyName = counterpartyName; this.participants = participants;
this.custodyName = null;
}
public CommitTrx(UtxoSignedTransaction signedTransaction, MemberX500Name counterpartyName, MemberX500Name custodyName) {
this.utxTrxCandidate = signedTransaction;
this.counterpartyName = counterpartyName;
this.custodyName = custodyName;
} }
@CordaInject @CordaInject
@ -53,20 +49,25 @@ public class CommitTrx implements SubFlow<SecureHash> {
public SecureHash call() { public SecureHash call() {
log.info("GameState commit started"); log.info("GameState commit started");
final MemberX500Name myName = memberLookup.myInfo().getName();
List<FlowSession> 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. * notarises the transaction and persists the transaction to each party's vault.
*/ */
final FlowSession session = flowMessaging.initiateFlow(this.counterpartyName);
List<FlowSession> sessionsList = new LinkedList<FlowSession>(Arrays.asList(session));
if (custodyName != null) {
sessionsList.add(flowMessaging.initiateFlow(custodyName));
}
final SecureHash trxId = ledgerService final SecureHash trxId = ledgerService
.finalize(this.utxTrxCandidate, sessionsList) .finalize(this.utxTrxCandidate, sessions)
.getTransaction() .getTransaction()
.getId(); .getId();