diff --git a/core/src/main/kotlin/com/r3corda/protocols/AbstractStateReplacementProtocol.kt b/core/src/main/kotlin/com/r3corda/protocols/AbstractStateReplacementProtocol.kt index 0a68e78895..79545db0f7 100644 --- a/core/src/main/kotlin/com/r3corda/protocols/AbstractStateReplacementProtocol.kt +++ b/core/src/main/kotlin/com/r3corda/protocols/AbstractStateReplacementProtocol.kt @@ -1,7 +1,9 @@ package com.r3corda.protocols import co.paralleluniverse.fibers.Suspendable -import com.r3corda.core.contracts.* +import com.r3corda.core.contracts.ContractState +import com.r3corda.core.contracts.StateAndRef +import com.r3corda.core.contracts.StateRef import com.r3corda.core.crypto.DigitalSignature import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.signWithECDSA @@ -12,6 +14,7 @@ import com.r3corda.core.random63BitValue import com.r3corda.core.transactions.SignedTransaction import com.r3corda.core.transactions.WireTransaction import com.r3corda.core.utilities.ProgressTracker +import com.r3corda.core.utilities.UntrustworthyData import com.r3corda.protocols.AbstractStateReplacementProtocol.Acceptor import com.r3corda.protocols.AbstractStateReplacementProtocol.Instigator import java.security.PublicKey @@ -119,7 +122,7 @@ abstract class AbstractStateReplacementProtocol { } } - abstract class Acceptor(val otherSide: Party, + abstract class Acceptor(val otherSide: Party, val sessionIdForSend: Long, val sessionIdForReceive: Long, override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic() { @@ -137,24 +140,21 @@ abstract class AbstractStateReplacementProtocol { @Suspendable override fun call() { progressTracker.currentStep = VERIFYING - val proposal = receive>(sessionIdForReceive).validate { it } - + val maybeProposal: UntrustworthyData> = receive(sessionIdForReceive) try { - verifyProposal(proposal) - verifyTx(proposal.stx) + val stx: SignedTransaction = maybeProposal.validate { verifyProposal(maybeProposal).stx } + verifyTx(stx) + approve(stx) } catch(e: Exception) { // TODO: catch only specific exceptions. However, there are numerous validation exceptions // that might occur (tx validation/resolution, invalid proposal). Need to rethink how // we manage exceptions and maybe introduce some platform exception hierarchy val myIdentity = serviceHub.storageService.myLegalIdentity - val state = proposal.stateRef + val state = maybeProposal.validate { it.stateRef } val reason = StateReplacementRefused(myIdentity, state, e.message) reject(reason) - return } - - approve(proposal.stx) } @Suspendable @@ -185,9 +185,10 @@ abstract class AbstractStateReplacementProtocol { /** * Check the state change proposal to confirm that it's acceptable to this node. Rules for verification depend - * on the change proposed, and may further depend on the node itself (for example configuration). + * on the change proposed, and may further depend on the node itself (for example configuration). The + * proposal is returned if acceptable, otherwise an exception is thrown. */ - abstract internal fun verifyProposal(proposal: Proposal) + abstract fun verifyProposal(maybeProposal: UntrustworthyData>): Proposal @Suspendable private fun verifyTx(stx: SignedTransaction) { diff --git a/core/src/main/kotlin/com/r3corda/protocols/NotaryChangeProtocol.kt b/core/src/main/kotlin/com/r3corda/protocols/NotaryChangeProtocol.kt index 487d11d33f..b3c99d8bb5 100644 --- a/core/src/main/kotlin/com/r3corda/protocols/NotaryChangeProtocol.kt +++ b/core/src/main/kotlin/com/r3corda/protocols/NotaryChangeProtocol.kt @@ -1,10 +1,16 @@ package com.r3corda.protocols import co.paralleluniverse.fibers.Suspendable -import com.r3corda.core.contracts.* +import com.r3corda.core.contracts.ContractState +import com.r3corda.core.contracts.StateAndRef +import com.r3corda.core.contracts.StateRef +import com.r3corda.core.contracts.TransactionType import com.r3corda.core.crypto.Party import com.r3corda.core.transactions.SignedTransaction import com.r3corda.core.utilities.ProgressTracker +import com.r3corda.core.utilities.UntrustworthyData +import com.r3corda.protocols.NotaryChangeProtocol.Acceptor +import com.r3corda.protocols.NotaryChangeProtocol.Instigator import java.security.PublicKey /** @@ -62,18 +68,24 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol() { * TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal */ @Suspendable - override fun verifyProposal(proposal: AbstractStateReplacementProtocol.Proposal) { - val newNotary = proposal.modification - val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.identity == newNotary } - require(isNotary) { "The proposed node $newNotary does not run a Notary service " } + override fun verifyProposal(maybeProposal: UntrustworthyData>): AbstractStateReplacementProtocol.Proposal { + return maybeProposal.validate { proposal -> + val newNotary = proposal.modification + val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.identity == newNotary } + require(isNotary) { "The proposed node $newNotary does not run a Notary service " } - val state = proposal.stateRef - val proposedTx = proposal.stx.tx - require(proposedTx.inputs.contains(state)) { "The proposed state $state is not in the proposed transaction inputs" } + val state = proposal.stateRef + val proposedTx = proposal.stx.tx + require(proposedTx.inputs.contains(state)) { "The proposed state $state is not in the proposed transaction inputs" } - // An example requirement - val blacklist = listOf("Evil Notary") - require(!blacklist.contains(newNotary.name)) { "The proposed new notary $newNotary is not trusted by the party" } + // An example requirement + val blacklist = listOf("Evil Notary") + require(!blacklist.contains(newNotary.name)) { "The proposed new notary $newNotary is not trusted by the party" } + + // TODO: Verify the type! + + proposal + } } } } \ No newline at end of file