diff --git a/Advanced/negotiation-cordapp/repositories.gradle b/Advanced/negotiation-cordapp/repositories.gradle index 9797c0ea..57d5c424 100644 --- a/Advanced/negotiation-cordapp/repositories.gradle +++ b/Advanced/negotiation-cordapp/repositories.gradle @@ -4,4 +4,28 @@ repositories { maven { url 'https://jitpack.io' } maven { url 'https://download.corda.net/maven/corda-dependencies' } maven { url 'https://repo.gradle.org/gradle/libs-releases' } + + // Repository where the user-reported artifact resides + maven { + url "https://software.r3.com/artifactory/r3-corda-releases" + credentials { + username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } + content { + includeGroupByRegex 'com\\.r3(\\..*)?' + } + } + + // Repository for corda-dev artifacts (contains net.corda SNAPSHOTs like corda-shell 4.14-SNAPSHOT) + maven { + url "https://software.r3.com/artifactory/corda-dev" + credentials { + username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } + content { + includeGroupByRegex 'net\\.corda(\\..*)?' + } + } } diff --git a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/AcceptanceFlow.java b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/AcceptanceFlow.java index 62426e2c..aa0c2dc8 100644 --- a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/AcceptanceFlow.java +++ b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/AcceptanceFlow.java @@ -3,6 +3,7 @@ import co.paralleluniverse.fibers.Suspendable; import com.google.common.collect.ImmutableList; +import net.corda.core.crypto.keyrotation.crossprovider.PartyIdentityResolver; import net.corda.samples.negotiation.contracts.ProposalAndTradeContract; import net.corda.samples.negotiation.states.ProposalState; import net.corda.samples.negotiation.states.TradeState; @@ -17,7 +18,6 @@ import net.corda.core.transactions.LedgerTransaction; import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.TransactionBuilder; -import net.corda.core.utilities.ProgressTracker; import org.jetbrains.annotations.NotNull; import java.security.PublicKey; @@ -31,7 +31,6 @@ public class AcceptanceFlow { public static class Initiator extends FlowLogic { private UniqueIdentifier proposalId; - private ProgressTracker progressTracker = new ProgressTracker(); public Initiator(UniqueIdentifier proposalId) { this.proposalId = proposalId; @@ -47,25 +46,31 @@ public SignedTransaction call() throws FlowException { ProposalState input = (ProposalState) inputStateAndRef.getState().getData(); - //Creating the output + // Creating the output TradeState output = new TradeState(input.getAmount(), input.getBuyer(), input.getSeller(), input.getLinearId()); - //Creating the command + // Creating the command List requiredSigners = ImmutableList.of(input.getProposee().getOwningKey(), input.getProposer().getOwningKey()); Command command = new Command(new ProposalAndTradeContract.Commands.Accept(), requiredSigners); - //Building the transaction + // Building the transaction Party notary = inputStateAndRef.getState().getNotary(); TransactionBuilder txBuilder = new TransactionBuilder(notary) .addInputState(inputStateAndRef) .addOutputState(output, ProposalAndTradeContract.ID) .addCommand(command); - //Signing the transaction ourselves + // Signing the transaction ourselves SignedTransaction partStx = getServiceHub().signInitialTransaction(txBuilder); - //Gathering the counterparty's signature - Party counterparty = (getOurIdentity().equals(input.getProposer()))? input.getProposee() : input.getProposer(); + // Gathering the counterparty's signature + // + // The proposer must be resolved to its latest identity before it can be compared with the party returned by `getOurIdentity`. + // + // The counterparty might be an old key, but the session will be initiated with the most up-to-date identity. + // No need to use the resolved party in this case. + Party proposer = PartyIdentityResolver.Companion.resolveToCurrentParty(input.getProposer(), getServiceHub().getIdentityService()); + Party counterparty = (getOurIdentity().equals(proposer))? input.getProposee() : input.getProposer(); FlowSession counterpartySession = initiateFlow(counterparty); SignedTransaction fullyStx = subFlow(new CollectSignaturesFlow(partStx, ImmutableList.of(counterpartySession))); @@ -92,7 +97,10 @@ public SignedTransaction call() throws FlowException { protected void checkTransaction(@NotNull SignedTransaction stx) throws FlowException { try { LedgerTransaction ledgerTx = stx.toLedgerTransaction(getServiceHub(), false); - Party proposee = ledgerTx.inputsOfType(ProposalState.class).get(0).getProposee(); + + // The proposee must be resolved to its latest identity before it can be compared with the party returned by `counterpartySession`. + // `counterpartySession` always returns the most up-to-date identity of the counterparty. + Party proposee = PartyIdentityResolver.Companion.resolveToCurrentParty(ledgerTx.inputsOfType(ProposalState.class).get(0).getProposee(), getServiceHub().getIdentityService()); if(!proposee.equals(counterpartySession.getCounterparty())){ throw new FlowException("Only the proposee can accept a proposal."); } diff --git a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ModificationFlow.java b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ModificationFlow.java index 2d6e8d39..e84028bb 100644 --- a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ModificationFlow.java +++ b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ModificationFlow.java @@ -2,6 +2,7 @@ import co.paralleluniverse.fibers.Suspendable; import com.google.common.collect.ImmutableList; +import net.corda.core.crypto.keyrotation.crossprovider.PartyIdentityResolver; import net.corda.samples.negotiation.contracts.ProposalAndTradeContract; import net.corda.samples.negotiation.states.ProposalState; import net.corda.core.contracts.Command; @@ -29,7 +30,6 @@ public class ModificationFlow { public static class Initiator extends FlowLogic{ private UniqueIdentifier proposalId; private int newAmount; - private ProgressTracker progressTracker = new ProgressTracker(); public Initiator(UniqueIdentifier proposalId, int newAmount) { this.proposalId = proposalId; @@ -44,8 +44,16 @@ public SignedTransaction call() throws FlowException { ProposalState input = (ProposalState) inputStateAndRef.getState().getData(); //Creating the output - Party counterparty = (getOurIdentity().equals(input.getProposer()))? input.getProposee() : input.getProposer(); - ProposalState output = new ProposalState(newAmount, input.getBuyer(),input.getSeller(), getOurIdentity(), counterparty, input.getLinearId()); + // + // The proposerParty is retrieved from the state and must be resolved to its latest identity + // before it can be compared with the party returned by `getOurIdentity`. Since the intent is not to + // replace the old key with the new one in the output state, calling `resolveToCurrentParty` is sufficient. + // + // Comparing parties that both originate from states is safe without additional resolution. + Party proposerParty = PartyIdentityResolver.Companion.resolveToCurrentParty(input.getProposer(), getServiceHub().getIdentityService()); + Party myPartyFromInput = (getOurIdentity().equals(proposerParty)) ? input.getProposer() : input.getProposee(); + Party counterpartyFromInput = (myPartyFromInput.equals(input.getProposer()))? input.getProposee() : input.getProposer(); + ProposalState output = new ProposalState(newAmount, input.getBuyer(),input.getSeller(), myPartyFromInput, counterpartyFromInput, input.getLinearId()); //Creating the command List requiredSigners = ImmutableList.of(input.getProposee().getOwningKey(), input.getProposer().getOwningKey()); @@ -62,7 +70,10 @@ public SignedTransaction call() throws FlowException { SignedTransaction partStx = getServiceHub().signInitialTransaction(txBuilder); //Gathering the counterparty's signatures - FlowSession counterpartySession = initiateFlow(counterparty); + // + // The counterparty might be an old key, but the session will be initiated with the most up-to-date identity. + // No need to use the resolved party in this case. + FlowSession counterpartySession = initiateFlow(counterpartyFromInput); SignedTransaction fullyStx = subFlow(new CollectSignaturesFlow(partStx, ImmutableList.of(counterpartySession))); //Finalising the transaction @@ -88,7 +99,15 @@ public SignedTransaction call() throws FlowException { protected void checkTransaction(@NotNull SignedTransaction stx) throws FlowException { try { LedgerTransaction ledgerTx = stx.toLedgerTransaction(getServiceHub(), false); - Party proposee = ledgerTx.inputsOfType(ProposalState.class).get(0).getProposee(); + ProposalState input = ledgerTx.inputsOfType(ProposalState.class).get(0); + + // The counterparty session always provides the most up-to-date identity for the counterparty. + // + // Therefore, any party retrieved from a state must be resolved using `resolveToCurrentParty`. + // While `resolveToCurrentParty` does not rely on a proof, it resolves the party to its latest valid identity. + // + // This ensures that equality checks behave as expected after key rotation. + Party proposee = PartyIdentityResolver.Companion.resolveToCurrentParty(input.getProposee(), getServiceHub().getIdentityService()); if(!proposee.equals(counterpartySession.getCounterparty())){ throw new FlowException("Only the proposee can modify a proposal."); } diff --git a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ProposalFlow.java b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ProposalFlow.java index 45def7ee..9d1f5b93 100644 --- a/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ProposalFlow.java +++ b/Advanced/negotiation-cordapp/workflows/src/main/java/net/corda/samples/negotiation/flows/ProposalFlow.java @@ -53,7 +53,7 @@ public UniqueIdentifier call() throws FlowException { // Obtain a reference to a notary we wish to use. /** Explicit selection of notary by CordaX500Name - argument can by coded in flows or parsed from config (Preferred)*/ - final Party notary = getServiceHub().getNetworkMapCache().getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB")); + final Party notary = getServiceHub().getNetworkMapCache().getNotary(CordaX500Name.parse("O=TestNotaryService, L=London, C=GB")); TransactionBuilder txBuilder = new TransactionBuilder(notary) .addOutputState(output, ProposalAndTradeContract.ID)