State replacement protocols: preserve the UntrustworthyData<T> marker for longer to try and get subclass authors in the right frame of mind.

This commit is contained in:
Mike Hearn 2016-09-06 15:52:04 +02:00
parent b89f86d013
commit 793e912324
2 changed files with 36 additions and 23 deletions

View File

@ -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<T> {
}
}
abstract class Acceptor<in T>(val otherSide: Party,
abstract class Acceptor<T>(val otherSide: Party,
val sessionIdForSend: Long,
val sessionIdForReceive: Long,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<Unit>() {
@ -137,24 +140,21 @@ abstract class AbstractStateReplacementProtocol<T> {
@Suspendable
override fun call() {
progressTracker.currentStep = VERIFYING
val proposal = receive<Proposal<T>>(sessionIdForReceive).validate { it }
val maybeProposal: UntrustworthyData<Proposal<T>> = 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<T> {
/**
* 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<T>)
abstract fun verifyProposal(maybeProposal: UntrustworthyData<Proposal<T>>): Proposal<T>
@Suspendable
private fun verifyTx(stx: SignedTransaction) {

View File

@ -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<Party>() {
* 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<Party>) {
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<Party>>): AbstractStateReplacementProtocol.Proposal<Party> {
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
}
}
}
}