Merged in rnicoll-upgrade-infrastructure (pull request #193)

Infrastructure ahead of contract upgrade support
This commit is contained in:
Ross Nicoll 2016-06-29 17:26:44 +01:00
commit b3af0ce218
11 changed files with 309 additions and 215 deletions

View File

@ -5,11 +5,12 @@ import com.r3corda.contracts.cash.CASH_PROGRAM_ID
import com.r3corda.contracts.cash.Cash
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.Contract
import com.r3corda.core.contracts.ContractState
import com.r3corda.core.contracts.DUMMY_PROGRAM_ID
import com.r3corda.core.contracts.DummyContract
import com.r3corda.core.contracts.DummyState
import com.r3corda.core.contracts.PartyAndReference
import com.r3corda.core.contracts.Issued
import com.r3corda.core.contracts.ContractState
import com.r3corda.core.contracts.TransactionState
import com.r3corda.core.crypto.NullPublicKey
import com.r3corda.core.crypto.Party
@ -31,7 +32,7 @@ val TEST_PROGRAM_MAP: Map<Contract, Class<out Contract>> = mapOf(
IRS_PROGRAM_ID to InterestRateSwap::class.java
)
fun generateState() = DummyContract.State(Random().nextInt())
fun generateState() = DummyState(Random().nextInt())
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//

View File

@ -80,7 +80,7 @@ class ObligationTests {
fun `issue debt`() {
// Check we can't "move" debt into existence.
transaction {
input { DummyContract.State() }
input { DummyState() }
output { outState }
arg(MINI_CORP_PUBKEY) { Obligation.Commands.Move(outState.issuanceDef) }

View File

@ -66,7 +66,7 @@ class CashTests {
fun issueMoney() {
// Check we can't "move" money into existence.
transaction {
input { DummyContract.State() }
input { DummyState() }
output { outState }
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }

View File

@ -9,13 +9,12 @@ import java.security.PublicKey
val DUMMY_PROGRAM_ID = DummyContract()
class DummyContract : Contract {
data class State(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
get() = emptyList()
interface State : ContractState {
val magicNumber: Int
}
data class SingleOwnerState(val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState {
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
get() = listOf(owner)
@ -23,8 +22,13 @@ class DummyContract : Contract {
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
}
data class MultiOwnerState(val magicNumber: Int = 0,
val owners: List<PublicKey>) : ContractState {
/**
* Alternative state with multiple owners. This exists primarily to provide a dummy state with multiple
* participants, and could in theory be merged with [SingleOwnerState] by putting the additional participants
* in a different field, however this is a good example of a contract with multiple states.
*/
data class MultiOwnerState(override val magicNumber: Int = 0,
val owners: List<PublicKey>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
get() = owners

View File

@ -0,0 +1,12 @@
package com.r3corda.core.contracts
import java.security.PublicKey
/**
* Dummy state for use in testing. Not part of any real contract.
*/
data class DummyState(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
get() = emptyList()
}

View File

@ -57,7 +57,7 @@ interface ContractState {
* is a miniature file system in which each file can be precisely mapped to the defining attachment.
*
* Attachments may contain many things (data files, legal documents, etc) but mostly they contain JVM bytecode.
* The classfiles inside define not only [Contract] implementations but also the classes that define the states.
* The class files inside define not only [Contract] implementations but also the classes that define the states.
* Within the rest of a transaction, user-providable components are referenced by name only.
*
* This means that a smart contract in Corda does two things:

View File

@ -0,0 +1,244 @@
package protocols
import co.paralleluniverse.fibers.Suspendable
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.DigitalSignature
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.signWithECDSA
import com.r3corda.core.messaging.Ack
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.NodeInfo
import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.core.random63BitValue
import com.r3corda.core.utilities.ProgressTracker
import com.r3corda.protocols.AbstractRequestMessage
import com.r3corda.protocols.NotaryProtocol
import com.r3corda.protocols.ResolveTransactionsProtocol
import java.security.PublicKey
/**
* Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state.
* Notably this requires a one to one replacement of states, states cannot be split, merged or issued as part of these
* protocols.
*
* The [Instigator] assembles the transaction for state replacement and sends out change proposals to all participants
* ([Acceptor]) of that state. If participants agree to the proposed change, they each sign the transaction.
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
* use the new updated state for future transactions.
*/
abstract class AbstractStateReplacementProtocol<T> {
interface Proposal<T> {
val stateRef: StateRef
val modification: T
val stx: SignedTransaction
}
class Handshake(val sessionIdForSend: Long,
replyTo: SingleMessageRecipient,
replySessionId: Long) : AbstractRequestMessage(replyTo, replySessionId)
abstract class Instigator<S : ContractState, T>(val originalState: StateAndRef<S>,
val modification: T,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<StateAndRef<S>>() {
companion object {
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
object NOTARY : ProgressTracker.Step("Requesting notary signature")
fun tracker() = ProgressTracker(SIGNING, NOTARY)
}
abstract val TOPIC_CHANGE: String
abstract val TOPIC_INITIATE: String
@Suspendable
override fun call(): StateAndRef<S> {
val (stx, participants) = assembleTx()
progressTracker.currentStep = SIGNING
val myKey = serviceHub.storageService.myLegalIdentity.owningKey
val me = listOf(myKey)
val signatures = if (participants == me) {
listOf(getNotarySignature(stx))
} else {
collectSignatures(participants - me, stx)
}
val finalTx = stx + signatures
serviceHub.recordTransactions(listOf(finalTx))
return finalTx.tx.outRef(0)
}
abstract internal fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
abstract internal fun assembleTx(): Pair<SignedTransaction, List<PublicKey>>
@Suspendable
private fun collectSignatures(participants: List<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
val sessions = mutableMapOf<NodeInfo, Long>()
val participantSignatures = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?:
throw IllegalStateException("Participant $it to state $originalState not found on the network")
val sessionIdForSend = random63BitValue()
sessions[participantNode] = sessionIdForSend
getParticipantSignature(participantNode, stx, sessionIdForSend)
}
val allSignatures = participantSignatures + getNotarySignature(stx)
sessions.forEach { send(TOPIC_CHANGE, it.key.address, it.value, allSignatures) }
return allSignatures
}
@Suspendable
private fun getParticipantSignature(node: NodeInfo, stx: SignedTransaction, sessionIdForSend: Long): DigitalSignature.WithKey {
val sessionIdForReceive = random63BitValue()
val proposal = assembleProposal(originalState.ref, modification, stx)
val handshake = Handshake(sessionIdForSend, serviceHub.networkService.myAddress, sessionIdForReceive)
sendAndReceive<Ack>(TOPIC_INITIATE, node.address, 0, sessionIdForReceive, handshake)
val response = sendAndReceive<Result>(TOPIC_CHANGE, node.address, sessionIdForSend, sessionIdForReceive, proposal)
val participantSignature = response.validate {
if (it.sig == null) throw StateReplacementException(it.error!!)
else {
check(it.sig.by == node.identity.owningKey) { "Not signed by the required participant" }
it.sig.verifyWithECDSA(stx.txBits)
it.sig
}
}
return participantSignature
}
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
}
}
abstract class Acceptor<T>(val otherSide: SingleMessageRecipient,
val sessionIdForSend: Long,
val sessionIdForReceive: Long,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<Unit>() {
companion object {
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")
object APPROVING : ProgressTracker.Step("State replacement approved")
object REJECTING : ProgressTracker.Step("State replacement rejected")
fun tracker() = ProgressTracker(VERIFYING, APPROVING, REJECTING)
}
abstract val TOPIC_CHANGE: String
abstract val TOPIC_INITIATE: String
@Suspendable
override fun call() {
progressTracker.currentStep = VERIFYING
val proposal = receive<Proposal<T>>(TOPIC_CHANGE, sessionIdForReceive).validate { it }
try {
verifyProposal(proposal)
verifyTx(proposal.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 reason = StateReplacementRefused(myIdentity, state, e.message)
reject(reason)
return
}
approve(proposal.stx)
}
@Suspendable
private fun approve(stx: SignedTransaction) {
progressTracker.currentStep = APPROVING
val mySignature = sign(stx)
val response = Result.noError(mySignature)
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(TOPIC_CHANGE, otherSide, sessionIdForSend, sessionIdForReceive, response)
val allSignatures = swapSignatures.validate { signatures ->
signatures.forEach { it.verifyWithECDSA(stx.txBits) }
signatures
}
val finalTx = stx + allSignatures
finalTx.verify()
serviceHub.recordTransactions(listOf(finalTx))
}
@Suspendable
private fun reject(e: StateReplacementRefused) {
progressTracker.currentStep = REJECTING
val response = Result.withError(e)
send(TOPIC_CHANGE, otherSide, sessionIdForSend, response)
}
/**
* 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).
*/
abstract internal fun verifyProposal(proposal: Proposal<T>)
@Suspendable
private fun verifyTx(stx: SignedTransaction) {
checkMySignatureRequired(stx.tx)
checkDependenciesValid(stx)
checkValid(stx)
}
private fun checkMySignatureRequired(tx: WireTransaction) {
// TODO: use keys from the keyManagementService instead
val myKey = serviceHub.storageService.myLegalIdentity.owningKey
require(tx.signers.contains(myKey)) { "Party is not a participant for any of the input states of transaction ${tx.id}" }
}
@Suspendable
private fun checkDependenciesValid(stx: SignedTransaction) {
val dependencyTxIDs = stx.tx.inputs.map { it.txhash }.toSet()
subProtocol(ResolveTransactionsProtocol(dependencyTxIDs, otherSide))
}
private fun checkValid(stx: SignedTransaction) {
val ltx = stx.tx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments)
serviceHub.verifyTransaction(ltx)
}
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
val myKeyPair = serviceHub.storageService.myLegalIdentityKey
return myKeyPair.signWithECDSA(stx.txBits)
}
}
// TODO: similar classes occur in other places (NotaryProtocol), need to consolidate
data class Result private constructor(val sig: DigitalSignature.WithKey?, val error: StateReplacementRefused?) {
companion object {
fun withError(error: StateReplacementRefused) = Result(null, error)
fun noError(sig: DigitalSignature.WithKey) = Result(sig, null)
}
}
}
/** Thrown when a participant refuses proposed the state replacement */
class StateReplacementRefused(val identity: Party, val state: StateRef, val detail: String?) {
override fun toString(): String
= "A participant $identity refused to change state $state"
}
class StateReplacementException(val error: StateReplacementRefused)
: Exception("State change failed - ${error}")

View File

@ -25,53 +25,30 @@ import java.security.PublicKey
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
* use the new updated state for future transactions.
*/
object NotaryChangeProtocol {
object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
val TOPIC_INITIATE = "platform.notary.change.initiate"
val TOPIC_CHANGE = "platform.notary.change.execute"
data class Proposal(val stateRef: StateRef,
val newNotary: Party,
val stx: SignedTransaction)
data class Proposal(override val stateRef: StateRef,
override val modification: Party,
override val stx: SignedTransaction) : AbstractStateReplacementProtocol.Proposal<Party>
class Handshake(val sessionIdForSend: Long,
replyTo: SingleMessageRecipient,
replySessionId: Long) : AbstractRequestMessage(replyTo, replySessionId)
class Instigator<T : ContractState>(originalState: StateAndRef<T>,
newNotary: Party,
progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementProtocol.Instigator<T, Party>(originalState, newNotary, progressTracker) {
class Instigator<T : ContractState>(val originalState: StateAndRef<T>,
val newNotary: Party,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<StateAndRef<T>>() {
companion object {
override val TOPIC_CHANGE: String
get() = NotaryChangeProtocol.TOPIC_CHANGE
override val TOPIC_INITIATE: String
get() = NotaryChangeProtocol.TOPIC_INITIATE
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
= NotaryChangeProtocol.Proposal(stateRef, modification, stx)
object NOTARY : ProgressTracker.Step("Requesting current Notary signature")
fun tracker() = ProgressTracker(SIGNING, NOTARY)
}
@Suspendable
override fun call(): StateAndRef<T> {
val (stx, participants) = assembleTx()
progressTracker.currentStep = SIGNING
val myKey = serviceHub.storageService.myLegalIdentity.owningKey
val me = listOf(myKey)
val signatures = if (participants == me) {
listOf(getNotarySignature(stx))
} else {
collectSignatures(participants - me, stx)
}
val finalTx = stx + signatures
serviceHub.recordTransactions(listOf(finalTx))
return finalTx.tx.outRef(0)
}
private fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
val state = originalState.state
val newState = state.withNewNotary(newNotary)
val newState = state.withNewNotary(modification)
val participants = state.data.participants
val tx = TransactionType.NotaryChange.Builder().withItems(originalState, newState)
tx.signWith(serviceHub.storageService.myLegalIdentityKey)
@ -79,116 +56,17 @@ object NotaryChangeProtocol {
val stx = tx.toSignedTransaction(false)
return Pair(stx, participants)
}
@Suspendable
private fun collectSignatures(participants: List<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
val sessions = mutableMapOf<NodeInfo, Long>()
val participantSignatures = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?:
throw IllegalStateException("Participant $it to state $originalState not found on the network")
val sessionIdForSend = random63BitValue()
sessions[participantNode] = sessionIdForSend
getParticipantSignature(participantNode, stx, sessionIdForSend)
}
val allSignatures = participantSignatures + getNotarySignature(stx)
sessions.forEach { send(TOPIC_CHANGE, it.key.address, it.value, allSignatures) }
return allSignatures
}
@Suspendable
private fun getParticipantSignature(node: NodeInfo, stx: SignedTransaction, sessionIdForSend: Long): DigitalSignature.WithKey {
val sessionIdForReceive = random63BitValue()
val proposal = Proposal(originalState.ref, newNotary, stx)
val handshake = Handshake(sessionIdForSend, serviceHub.networkService.myAddress, sessionIdForReceive)
sendAndReceive<Ack>(TOPIC_INITIATE, node.address, 0, sessionIdForReceive, handshake)
val response = sendAndReceive<Result>(TOPIC_CHANGE, node.address, sessionIdForSend, sessionIdForReceive, proposal)
val participantSignature = response.validate {
if (it.sig == null) throw NotaryChangeException(it.error!!)
else {
check(it.sig.by == node.identity.owningKey) { "Not signed by the required participant" }
it.sig.verifyWithECDSA(stx.txBits)
it.sig
}
}
return participantSignature
}
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
}
}
class Acceptor(val otherSide: SingleMessageRecipient,
val sessionIdForSend: Long,
val sessionIdForReceive: Long,
override val progressTracker: ProgressTracker = tracker()) : ProtocolLogic<Unit>() {
companion object {
object VERIFYING : ProgressTracker.Step("Verifying Notary change proposal")
object APPROVING : ProgressTracker.Step("Notary change approved")
object REJECTING : ProgressTracker.Step("Notary change rejected")
fun tracker() = ProgressTracker(VERIFYING, APPROVING, REJECTING)
}
@Suspendable
override fun call() {
progressTracker.currentStep = VERIFYING
val proposal = receive<Proposal>(TOPIC_CHANGE, sessionIdForReceive).validate { it }
try {
verifyProposal(proposal)
verifyTx(proposal.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 reason = NotaryChangeRefused(myIdentity, state, e.message)
reject(reason)
return
}
approve(proposal.stx)
}
@Suspendable
private fun approve(stx: SignedTransaction) {
progressTracker.currentStep = APPROVING
val mySignature = sign(stx)
val response = Result.noError(mySignature)
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(TOPIC_CHANGE, otherSide, sessionIdForSend, sessionIdForReceive, response)
val allSignatures = swapSignatures.validate { signatures ->
signatures.forEach { it.verifyWithECDSA(stx.txBits) }
signatures
}
val finalTx = stx + allSignatures
finalTx.verify()
serviceHub.recordTransactions(listOf(finalTx))
}
@Suspendable
private fun reject(e: NotaryChangeRefused) {
progressTracker.currentStep = REJECTING
val response = Result.withError(e)
send(TOPIC_CHANGE, otherSide, sessionIdForSend, response)
}
class Acceptor(otherSide: SingleMessageRecipient,
sessionIdForSend: Long,
sessionIdForReceive: Long,
override val progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementProtocol.Acceptor<Party>(otherSide, sessionIdForSend, sessionIdForReceive) {
override val TOPIC_CHANGE: String
get() = NotaryChangeProtocol.TOPIC_CHANGE
override val TOPIC_INITIATE: String
get() = NotaryChangeProtocol.TOPIC_INITIATE
/**
* Check the notary change proposal.
@ -198,8 +76,8 @@ object NotaryChangeProtocol {
* TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal
*/
@Suspendable
private fun verifyProposal(proposal: NotaryChangeProtocol.Proposal) {
val newNotary = proposal.newNotary
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 " }
@ -211,51 +89,5 @@ object NotaryChangeProtocol {
val blacklist = listOf("Evil Notary")
require(!blacklist.contains(newNotary.name)) { "The proposed new notary $newNotary is not trusted by the party" }
}
@Suspendable
private fun verifyTx(stx: SignedTransaction) {
checkMySignatureRequired(stx.tx)
checkDependenciesValid(stx)
checkValid(stx)
}
private fun checkMySignatureRequired(tx: WireTransaction) {
// TODO: use keys from the keyManagementService instead
val myKey = serviceHub.storageService.myLegalIdentity.owningKey
require(tx.signers.contains(myKey)) { "Party is not a participant for any of the input states of transaction ${tx.id}" }
}
@Suspendable
private fun checkDependenciesValid(stx: SignedTransaction) {
val dependencyTxIDs = stx.tx.inputs.map { it.txhash }.toSet()
subProtocol(ResolveTransactionsProtocol(dependencyTxIDs, otherSide))
}
private fun checkValid(stx: SignedTransaction) {
val ltx = stx.tx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments)
serviceHub.verifyTransaction(ltx)
}
private fun sign(stx: SignedTransaction): DigitalSignature.WithKey {
val myKeyPair = serviceHub.storageService.myLegalIdentityKey
return myKeyPair.signWithECDSA(stx.txBits)
}
}
// TODO: similar classes occur in other places (NotaryProtocol), need to consolidate
data class Result private constructor(val sig: DigitalSignature.WithKey?, val error: NotaryChangeRefused?) {
companion object {
fun withError(error: NotaryChangeRefused) = Result(null, error)
fun noError(sig: DigitalSignature.WithKey) = Result(sig, null)
}
}
}
/** Thrown when a participant refuses to change the notary of the state */
class NotaryChangeRefused(val identity: Party, val state: StateRef, val cause: String?) {
override fun toString() = "A participant $identity refused to change the notary of state $state"
}
class NotaryChangeException(val error: NotaryChangeRefused) : Exception() {
override fun toString() = "${super.toString()}: Notary change failed - ${error.toString()}"
}

View File

@ -28,12 +28,12 @@ class TransactionGraphSearchTests {
*/
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage {
val originTx = TransactionType.General.Builder().apply {
addOutputState(DummyContract.State(random31BitValue()), DUMMY_NOTARY)
addOutputState(DummyState(random31BitValue()), DUMMY_NOTARY)
addCommand(command, signer.public)
signWith(signer)
}.toSignedTransaction(false)
val inputTx = TransactionType.General.Builder().apply {
addInputState(originTx.tx.outRef<DummyContract.State>(0))
addInputState(originTx.tx.outRef<DummyState>(0))
signWith(signer)
}.toSignedTransaction(false)
return GraphTransactionStorage(originTx, inputTx)

View File

@ -5,6 +5,7 @@ import com.r3corda.core.messaging.MessagingService
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.node.services.api.AbstractNodeService
import com.r3corda.node.services.statemachine.StateMachineManager
import protocols.AbstractStateReplacementProtocol
import protocols.NotaryChangeProtocol
/**
@ -14,11 +15,11 @@ import protocols.NotaryChangeProtocol
class NotaryChangeService(net: MessagingService, val smm: StateMachineManager) : AbstractNodeService(net) {
init {
addMessageHandler(NotaryChangeProtocol.TOPIC_INITIATE,
{ req: NotaryChangeProtocol.Handshake -> handleChangeNotaryRequest(req) }
{ req: AbstractStateReplacementProtocol.Handshake -> handleChangeNotaryRequest(req) }
)
}
private fun handleChangeNotaryRequest(req: NotaryChangeProtocol.Handshake): Ack {
private fun handleChangeNotaryRequest(req: AbstractStateReplacementProtocol.Handshake): Ack {
val protocol = NotaryChangeProtocol.Acceptor(
req.replyTo as SingleMessageRecipient,
req.sessionID!!,

View File

@ -11,10 +11,10 @@ import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.transactions.SimpleNotaryService
import org.junit.Before
import org.junit.Test
import protocols.NotaryChangeException
import protocols.StateReplacementException
import protocols.StateReplacementRefused
import protocols.NotaryChangeProtocol
import protocols.NotaryChangeProtocol.Instigator
import protocols.NotaryChangeRefused
import java.util.concurrent.ExecutionException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -80,8 +80,8 @@ class NotaryChangeTests {
net.runNetwork()
val ex = assertFailsWith(ExecutionException::class) { future.get() }
val error = (ex.cause as NotaryChangeException).error
assertTrue(error is NotaryChangeRefused)
val error = (ex.cause as StateReplacementException).error
assertTrue(error is StateReplacementRefused)
}
// TODO: Add more test cases once we have a general protocol/service exception handling mechanism: