Corda: GameProposal
This commit is contained in:
parent
43c4252df1
commit
c4048c20b6
@ -10,7 +10,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group 'com.r3.developers.csdetemplate'
|
group 'djmil.cordacheckers'
|
||||||
version '1.0-SNAPSHOT'
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
def javaVersion = VERSION_11
|
def javaVersion = VERSION_11
|
||||||
|
@ -67,9 +67,9 @@ cordapp {
|
|||||||
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
|
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
|
||||||
// Declares the type and metadata of the CPK (this CPB has one CPK).
|
// Declares the type and metadata of the CPK (this CPB has one CPK).
|
||||||
contract {
|
contract {
|
||||||
name "ContractsModuleNameHere"
|
name "CordaCkeckersContracts"
|
||||||
versionId 1
|
versionId 1
|
||||||
vendor "VendorNameHere"
|
vendor "djmil"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
package djmil.cordacheckers.contracts;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
import net.corda.v5.base.exceptions.CordaRuntimeException;
|
||||||
|
import net.corda.v5.ledger.utxo.Command;
|
||||||
|
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 class Create implements Command { }
|
||||||
|
public static class Accept implements Command { }
|
||||||
|
public static class Reject implements Command { }
|
||||||
|
public static class Cancle implements Command { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(UtxoLedgerTransaction trx) {
|
||||||
|
log.info("GameProposalContract.verify() called");
|
||||||
|
|
||||||
|
requireThat(trx.getCommands().size() == 1, REQUIRE_SINGLE_COMMAND);
|
||||||
|
Command command = trx.getCommands().get(0);
|
||||||
|
|
||||||
|
if (command instanceof Create) {
|
||||||
|
requireThat(trx.getInputContractStates().isEmpty(), CREATE_INPUT_STATE);
|
||||||
|
requireThat(trx.getOutputContractStates().size() == 1, CREATE_OUTPUT_STATE);
|
||||||
|
|
||||||
|
GameProposalState outputState = trx.getOutputStates(GameProposalState.class).get(0);
|
||||||
|
requireThat(outputState != null, CREATE_OUTPUT_STATE);
|
||||||
|
|
||||||
|
requireThat(outputState.getYouPlayAs() != null, CREATE_NOT_NULL_YOU_PLAY_AS);
|
||||||
|
} else
|
||||||
|
if (command instanceof Accept) {
|
||||||
|
// TODO outputState -> Game
|
||||||
|
throw new CordaRuntimeException("Unimplemented!");
|
||||||
|
} else
|
||||||
|
if (command instanceof Reject) {
|
||||||
|
requireThat(trx.getInputContractStates().size() == 1, REJECT_INPUT_STATE);
|
||||||
|
requireThat(trx.getOutputContractStates().isEmpty(), REJECT_OUTPUT_STATE);
|
||||||
|
|
||||||
|
GameProposalState inputState = trx.getInputStates(GameProposalState.class).get(0);
|
||||||
|
requireThat(inputState != null, REJECT_INPUT_STATE);
|
||||||
|
} else
|
||||||
|
if (command instanceof Cancle) {
|
||||||
|
// TODO cancle game state
|
||||||
|
throw new CordaRuntimeException("Unimplemented!");
|
||||||
|
} else {
|
||||||
|
throw new CordaRuntimeException(UNKNOWN_COMMAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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 CREATE_INPUT_STATE = "Create command should have no input states";
|
||||||
|
static final String CREATE_OUTPUT_STATE = "Create command should output exactly one GameProposal state";
|
||||||
|
static final String CREATE_NOT_NULL_YOU_PLAY_AS = "GameProposal.youPlayAs field can not be null";
|
||||||
|
|
||||||
|
static final String REJECT_INPUT_STATE = "Reject command should have exactly one GameProposal state";
|
||||||
|
static final String REJECT_OUTPUT_STATE = "Reject command should have no output states";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package djmil.cordacheckers.states;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
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 GameProposalState implements ContractState {
|
||||||
|
|
||||||
|
@CordaSerializable
|
||||||
|
public enum Color {
|
||||||
|
WHITE,
|
||||||
|
BLACK,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final MemberX500Name sender;
|
||||||
|
public final MemberX500Name recipient;
|
||||||
|
public final Color youPlayAs;
|
||||||
|
public final String additionalMessage;
|
||||||
|
public final UUID id;
|
||||||
|
public final List<PublicKey> participants;
|
||||||
|
|
||||||
|
// Allows serialisation and to use a specified UUID
|
||||||
|
@ConstructorForDeserialization
|
||||||
|
public GameProposalState(
|
||||||
|
MemberX500Name sender,
|
||||||
|
MemberX500Name recipient,
|
||||||
|
Color youPlayAs,
|
||||||
|
String additionalMessage,
|
||||||
|
UUID id,
|
||||||
|
List<PublicKey> participants) {
|
||||||
|
this.sender = sender;
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.youPlayAs = youPlayAs;
|
||||||
|
this.additionalMessage = additionalMessage;
|
||||||
|
this.id = id;
|
||||||
|
this.participants = participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberX500Name getRecipient() {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getYouPlayAs() {
|
||||||
|
return youPlayAs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdditionalMessage() {
|
||||||
|
return additionalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PublicKey> getParticipants() {
|
||||||
|
return participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRecipientCommonName() {
|
||||||
|
return recipient == null ? "" : recipient.getCommonName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderCommonName() {
|
||||||
|
return sender == null ? "" : sender.getCommonName();
|
||||||
|
}
|
||||||
|
}
|
@ -69,9 +69,9 @@ cordapp {
|
|||||||
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
|
// The cordapp section contains either a workflow or contract subsection depending on the type of component.
|
||||||
// Declares the type and metadata of the CPK (this CPB has one CPK).
|
// Declares the type and metadata of the CPK (this CPB has one CPK).
|
||||||
workflow {
|
workflow {
|
||||||
name "WorkflowsModuleNameHere"
|
name "CordaCkeckers"
|
||||||
versionId 1
|
versionId 1
|
||||||
vendor "VendorNameHere"
|
vendor "djmil"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.contracts.GameProposalContract;
|
||||||
|
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.flows.FlowEngine;
|
||||||
|
import net.corda.v5.application.flows.InitiatingFlow;
|
||||||
|
import net.corda.v5.application.marshalling.JsonMarshallingService;
|
||||||
|
import net.corda.v5.application.membership.MemberLookup;
|
||||||
|
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.common.NotaryLookup;
|
||||||
|
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder;
|
||||||
|
import net.corda.v5.membership.MemberInfo;
|
||||||
|
import net.corda.v5.membership.NotaryInfo;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@InitiatingFlow(protocol = "game-proposal-create")
|
||||||
|
public class CreateFlow implements ClientStartableFlow{
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(CreateFlow.class);
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public NotaryLookup notaryLookup;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public UtxoLedgerService ledgerService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public FlowEngine flowEngine;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public FlowMessaging flowMessaging;
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
@Override
|
||||||
|
public String call(ClientRequestBody requestBody) {
|
||||||
|
try {
|
||||||
|
log.info("flow: Create Game Proposal");
|
||||||
|
|
||||||
|
GameProposalState gameProposal = buildGameProposalStateFrom(requestBody);
|
||||||
|
String result = doTrunsaction(gameProposal);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
log.warn("CreateGameProposal flow failed to process utxo request body " + requestBody + " because: " + e.getMessage());
|
||||||
|
throw new CordaRuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private GameProposalState buildGameProposalStateFrom(ClientRequestBody requestBody) {
|
||||||
|
CreateFlowArgs args = requestBody.getRequestBodyAs(jsonMarshallingService, CreateFlowArgs.class);
|
||||||
|
|
||||||
|
GameProposalState.Color opponentColor = GameProposalState.Color.valueOf(args.opponentColor);
|
||||||
|
if (opponentColor == null) {
|
||||||
|
throw new RuntimeException("Allowed values for opponentColor are: "
|
||||||
|
+ GameProposalState.Color.WHITE.name() +", " + GameProposalState.Color.BLACK.name()
|
||||||
|
+ ". Actual value was: " + args.opponentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberInfo myInfo = memberLookup.myInfo();
|
||||||
|
MemberInfo opponentInfo = requireNonNull(
|
||||||
|
memberLookup.lookup(MemberX500Name.parse(args.opponentName)),
|
||||||
|
"MemberLookup can't find opponentName specified in flow arguments: " + args.opponentName
|
||||||
|
);
|
||||||
|
|
||||||
|
return new GameProposalState(
|
||||||
|
myInfo.getName(),
|
||||||
|
opponentInfo.getName(),
|
||||||
|
GameProposalState.Color.valueOf(args.opponentColor),
|
||||||
|
args.additionalMessage,
|
||||||
|
UUID.randomUUID(),
|
||||||
|
Arrays.asList(myInfo.getLedgerKeys().get(0), opponentInfo.getLedgerKeys().get(0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private String doTrunsaction(GameProposalState gameProposal) {
|
||||||
|
NotaryInfo notary = notaryLookup.getNotaryServices().iterator().next();
|
||||||
|
|
||||||
|
UtxoTransactionBuilder txBuilder = ledgerService.createTransactionBuilder()
|
||||||
|
.setNotary(notary.getName())
|
||||||
|
.setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis()))
|
||||||
|
.addOutputState(gameProposal)
|
||||||
|
.addCommand(new GameProposalContract.Create())
|
||||||
|
.addSignatories(gameProposal.getParticipants());
|
||||||
|
|
||||||
|
UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction();
|
||||||
|
|
||||||
|
FlowSession session = flowMessaging.initiateFlow(gameProposal.getRecipient());
|
||||||
|
|
||||||
|
List<FlowSession> sessionsList = Arrays.asList(session);
|
||||||
|
|
||||||
|
UtxoSignedTransaction finalizedSignedTransaction = ledgerService
|
||||||
|
.finalize(signedTransaction, sessionsList)
|
||||||
|
.getTransaction();
|
||||||
|
|
||||||
|
return finalizedSignedTransaction.getId().toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
public class CreateFlowArgs {
|
||||||
|
public final String opponentName;
|
||||||
|
public final String opponentColor;
|
||||||
|
public final String additionalMessage;
|
||||||
|
|
||||||
|
public CreateFlowArgs(String opponentName, String opponentColor, String additionalMessage) {
|
||||||
|
this.opponentName = opponentName;
|
||||||
|
this.opponentColor = opponentColor;
|
||||||
|
this.additionalMessage = additionalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialisation service requires a default constructor
|
||||||
|
public CreateFlowArgs() {
|
||||||
|
this(null, null, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
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.UtxoSignedTransaction;
|
||||||
|
import net.corda.v5.ledger.utxo.transaction.UtxoTransactionValidator;
|
||||||
|
|
||||||
|
@InitiatedBy(protocol = "game-proposal-create")
|
||||||
|
public class CreateResponder implements ResponderFlow {
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(CreateResponder.class);
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public MemberLookup memberLookup;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public UtxoLedgerService utxoLedgerService;
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
@Override
|
||||||
|
public void call(FlowSession session) {
|
||||||
|
try {
|
||||||
|
// Defines the lambda validator used in receiveFinality below.
|
||||||
|
UtxoTransactionValidator txValidator = ledgerTransaction -> {
|
||||||
|
GameProposalState gameProposal = (GameProposalState) ledgerTransaction.getOutputContractStates().get(0);
|
||||||
|
// Uses checkForBannedWords() and checkMessageFromMatchesCounterparty() functions
|
||||||
|
// to check whether to sign the transaction.
|
||||||
|
if (!checkParticipants(gameProposal, session.getCounterparty())) {
|
||||||
|
throw new CordaRuntimeException("Failed verification");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
Boolean checkParticipants(GameProposalState gameProposal, MemberX500Name counterpartyName) {
|
||||||
|
MemberX500Name myName = memberLookup.myInfo().getName();
|
||||||
|
|
||||||
|
if (gameProposal.getRecipient().compareTo(myName) == 0 &&
|
||||||
|
gameProposal.getSender().compareTo(counterpartyName) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
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.ledger.utxo.StateAndRef;
|
||||||
|
import net.corda.v5.ledger.utxo.UtxoLedgerService;
|
||||||
|
|
||||||
|
public class ListFlow implements ClientStartableFlow {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(ListFlow.class);
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public UtxoLedgerService utxoLedgerService;
|
||||||
|
|
||||||
|
@CordaInject
|
||||||
|
public JsonMarshallingService jsonMarshallingService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call(ClientRequestBody requestBody) {
|
||||||
|
log.info("ListChatsFlow.call() called");
|
||||||
|
|
||||||
|
// Queries the VNode's vault for unconsumed states and converts the result to a serializable DTO.
|
||||||
|
java.util.List<StateAndRef<GameProposalState>> states = utxoLedgerService.findUnconsumedStatesByType(GameProposalState.class);
|
||||||
|
|
||||||
|
java.util.List<ListItem> results = states.stream().map( stateAndRef ->
|
||||||
|
new ListItem(stateAndRef.getState().getContractState())
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Uses the JsonMarshallingService's format() function to serialize the DTO to Json.
|
||||||
|
return jsonMarshallingService.format(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import djmil.cordacheckers.states.GameProposalState;
|
||||||
|
|
||||||
|
// Class to hold results of the List flow.
|
||||||
|
// The GameProposal(s) cannot be returned directly as the 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 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 youPlayAs;
|
||||||
|
public final String additionalMessage;
|
||||||
|
public final UUID id;
|
||||||
|
|
||||||
|
// Serialisation service requires a default constructor
|
||||||
|
public ListItem() {
|
||||||
|
this.sender = null;
|
||||||
|
this.recipient = null;
|
||||||
|
this.youPlayAs = null;
|
||||||
|
this.additionalMessage = null;
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListItem(GameProposalState state) {
|
||||||
|
this.sender = state.getSenderCommonName();
|
||||||
|
this.recipient = state.getRecipientCommonName();
|
||||||
|
this.youPlayAs = state.getYouPlayAs().name();
|
||||||
|
this.additionalMessage = state.getAdditionalMessage();
|
||||||
|
this.id = state.getId();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package djmil.cordacheckers.gameproposal;
|
||||||
|
|
||||||
|
public class RejectFlow {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user