mirror of
https://github.com/corda/corda.git
synced 2025-03-11 06:54:04 +00:00
Merged in notary-check-signatures (pull request #173)
Validating notary: check for missing signatures
This commit is contained in:
commit
497fcabd4d
@ -97,7 +97,7 @@ object TwoPartyTradeProtocol {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
||||||
progressTracker.currentStep = NOTARY
|
progressTracker.currentStep = NOTARY
|
||||||
return subProtocol(NotaryProtocol.Client(stx.tx))
|
return subProtocol(NotaryProtocol.Client(stx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.r3corda.protocols
|
package com.r3corda.protocols
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import com.r3corda.core.contracts.SignedTransaction
|
||||||
import com.r3corda.core.contracts.TimestampCommand
|
import com.r3corda.core.contracts.TimestampCommand
|
||||||
import com.r3corda.core.contracts.WireTransaction
|
import com.r3corda.core.contracts.WireTransaction
|
||||||
import com.r3corda.core.crypto.DigitalSignature
|
import com.r3corda.core.crypto.DigitalSignature
|
||||||
@ -17,7 +18,6 @@ import com.r3corda.core.noneOrSingle
|
|||||||
import com.r3corda.core.protocols.ProtocolLogic
|
import com.r3corda.core.protocols.ProtocolLogic
|
||||||
import com.r3corda.core.random63BitValue
|
import com.r3corda.core.random63BitValue
|
||||||
import com.r3corda.core.serialization.SerializedBytes
|
import com.r3corda.core.serialization.SerializedBytes
|
||||||
import com.r3corda.core.serialization.deserialize
|
|
||||||
import com.r3corda.core.serialization.serialize
|
import com.r3corda.core.serialization.serialize
|
||||||
import com.r3corda.core.utilities.ProgressTracker
|
import com.r3corda.core.utilities.ProgressTracker
|
||||||
import com.r3corda.core.utilities.UntrustworthyData
|
import com.r3corda.core.utilities.UntrustworthyData
|
||||||
@ -34,7 +34,7 @@ object NotaryProtocol {
|
|||||||
* @throws NotaryException in case the any of the inputs to the transaction have been consumed
|
* @throws NotaryException in case the any of the inputs to the transaction have been consumed
|
||||||
* by another transaction or the timestamp is invalid
|
* by another transaction or the timestamp is invalid
|
||||||
*/
|
*/
|
||||||
class Client(private val wtx: WireTransaction,
|
class Client(private val stx: SignedTransaction,
|
||||||
override val progressTracker: ProgressTracker = Client.tracker()) : ProtocolLogic<DigitalSignature.LegallyIdentifiable>() {
|
override val progressTracker: ProgressTracker = Client.tracker()) : ProtocolLogic<DigitalSignature.LegallyIdentifiable>() {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ object NotaryProtocol {
|
|||||||
val handshake = Handshake(serviceHub.networkService.myAddress, sendSessionID, receiveSessionID)
|
val handshake = Handshake(serviceHub.networkService.myAddress, sendSessionID, receiveSessionID)
|
||||||
sendAndReceive<Ack>(TOPIC_INITIATE, notaryNode.address, 0, receiveSessionID, handshake)
|
sendAndReceive<Ack>(TOPIC_INITIATE, notaryNode.address, 0, receiveSessionID, handshake)
|
||||||
|
|
||||||
val request = SignRequest(wtx.serialized, serviceHub.storageService.myLegalIdentity)
|
val request = SignRequest(stx, serviceHub.storageService.myLegalIdentity)
|
||||||
val response = sendAndReceive<Result>(TOPIC, notaryNode.address, sendSessionID, receiveSessionID, request)
|
val response = sendAndReceive<Result>(TOPIC, notaryNode.address, sendSessionID, receiveSessionID, request)
|
||||||
|
|
||||||
val notaryResult = validateResponse(response)
|
val notaryResult = validateResponse(response)
|
||||||
@ -69,7 +69,7 @@ object NotaryProtocol {
|
|||||||
progressTracker.currentStep = VALIDATING
|
progressTracker.currentStep = VALIDATING
|
||||||
|
|
||||||
response.validate {
|
response.validate {
|
||||||
if (it.sig != null) validateSignature(it.sig, wtx.serialized)
|
if (it.sig != null) validateSignature(it.sig, stx.txBits)
|
||||||
else if (it.error is NotaryError.Conflict) it.error.conflict.verified()
|
else if (it.error is NotaryError.Conflict) it.error.conflict.verified()
|
||||||
else if (it.error == null || it.error !is NotaryError)
|
else if (it.error == null || it.error !is NotaryError)
|
||||||
throw IllegalStateException("Received invalid result from Notary service '${notaryNode.identity}'")
|
throw IllegalStateException("Received invalid result from Notary service '${notaryNode.identity}'")
|
||||||
@ -84,6 +84,7 @@ object NotaryProtocol {
|
|||||||
|
|
||||||
private fun findNotaryNode(): NodeInfo {
|
private fun findNotaryNode(): NodeInfo {
|
||||||
var maybeNotaryKey: PublicKey? = null
|
var maybeNotaryKey: PublicKey? = null
|
||||||
|
val wtx = stx.tx
|
||||||
|
|
||||||
val timestampCommand = wtx.commands.singleOrNull { it.value is TimestampCommand }
|
val timestampCommand = wtx.commands.singleOrNull { it.value is TimestampCommand }
|
||||||
if (timestampCommand != null) maybeNotaryKey = timestampCommand.signers.first()
|
if (timestampCommand != null) maybeNotaryKey = timestampCommand.signers.first()
|
||||||
@ -117,17 +118,17 @@ object NotaryProtocol {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
val request = receive<SignRequest>(TOPIC, receiveSessionID).validate { it }
|
val request = receive<SignRequest>(TOPIC, receiveSessionID).validate { it }
|
||||||
val txBits = request.txBits
|
val stx = request.tx
|
||||||
|
val wtx = stx.tx
|
||||||
val reqIdentity = request.callerIdentity
|
val reqIdentity = request.callerIdentity
|
||||||
|
|
||||||
val wtx = txBits.deserialize()
|
|
||||||
val result: Result
|
val result: Result
|
||||||
try {
|
try {
|
||||||
validateTimestamp(wtx)
|
validateTimestamp(wtx)
|
||||||
beforeCommit(wtx, reqIdentity)
|
beforeCommit(stx, reqIdentity)
|
||||||
commitInputStates(wtx, reqIdentity)
|
commitInputStates(wtx, reqIdentity)
|
||||||
|
|
||||||
val sig = sign(txBits)
|
val sig = sign(stx.txBits)
|
||||||
result = Result.noError(sig)
|
result = Result.noError(sig)
|
||||||
|
|
||||||
} catch(e: NotaryException) {
|
} catch(e: NotaryException) {
|
||||||
@ -159,7 +160,7 @@ object NotaryProtocol {
|
|||||||
* undo the commit of the input states (the exact mechanism still needs to be worked out)
|
* undo the commit of the input states (the exact mechanism still needs to be worked out)
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
@Suspendable
|
||||||
open fun beforeCommit(wtx: WireTransaction, reqIdentity: Party) {
|
open fun beforeCommit(stx: SignedTransaction, reqIdentity: Party) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun commitInputStates(tx: WireTransaction, reqIdentity: Party) {
|
private fun commitInputStates(tx: WireTransaction, reqIdentity: Party) {
|
||||||
@ -185,7 +186,7 @@ object NotaryProtocol {
|
|||||||
sessionID: Long) : AbstractRequestMessage(replyTo, sessionID)
|
sessionID: Long) : AbstractRequestMessage(replyTo, sessionID)
|
||||||
|
|
||||||
/** TODO: The caller must authenticate instead of just specifying its identity */
|
/** TODO: The caller must authenticate instead of just specifying its identity */
|
||||||
class SignRequest(val txBits: SerializedBytes<WireTransaction>,
|
class SignRequest(val tx: SignedTransaction,
|
||||||
val callerIdentity: Party)
|
val callerIdentity: Party)
|
||||||
|
|
||||||
data class Result private constructor(val sig: DigitalSignature.LegallyIdentifiable?, val error: NotaryError?) {
|
data class Result private constructor(val sig: DigitalSignature.LegallyIdentifiable?, val error: NotaryError?) {
|
||||||
@ -232,4 +233,6 @@ sealed class NotaryError {
|
|||||||
class TimestampInvalid : NotaryError()
|
class TimestampInvalid : NotaryError()
|
||||||
|
|
||||||
class TransactionInvalid : NotaryError()
|
class TransactionInvalid : NotaryError()
|
||||||
|
|
||||||
|
class SignaturesMissing(val missingSigners: List<PublicKey>) : NotaryError()
|
||||||
}
|
}
|
@ -158,7 +158,7 @@ object TwoPartyDealProtocol {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
||||||
progressTracker.currentStep = NOTARY
|
progressTracker.currentStep = NOTARY
|
||||||
return subProtocol(NotaryProtocol.Client(stx.tx))
|
return subProtocol(NotaryProtocol.Client(stx))
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun signWithOurKey(partialTX: SignedTransaction): DigitalSignature.WithKey {
|
open fun signWithOurKey(partialTX: SignedTransaction): DigitalSignature.WithKey {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.r3corda.protocols
|
package com.r3corda.protocols
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import com.r3corda.core.contracts.SignedTransaction
|
||||||
import com.r3corda.core.contracts.TransactionVerificationException
|
import com.r3corda.core.contracts.TransactionVerificationException
|
||||||
import com.r3corda.core.contracts.WireTransaction
|
import com.r3corda.core.contracts.WireTransaction
|
||||||
import com.r3corda.core.contracts.toLedgerTransaction
|
import com.r3corda.core.contracts.toLedgerTransaction
|
||||||
@ -22,8 +23,10 @@ class ValidatingNotaryProtocol(otherSide: SingleMessageRecipient,
|
|||||||
timestampChecker: TimestampChecker,
|
timestampChecker: TimestampChecker,
|
||||||
uniquenessProvider: UniquenessProvider) : NotaryProtocol.Service(otherSide, sessionIdForSend, sessionIdForReceive, timestampChecker, uniquenessProvider) {
|
uniquenessProvider: UniquenessProvider) : NotaryProtocol.Service(otherSide, sessionIdForSend, sessionIdForReceive, timestampChecker, uniquenessProvider) {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun beforeCommit(wtx: WireTransaction, reqIdentity: Party) {
|
override fun beforeCommit(stx: SignedTransaction, reqIdentity: Party) {
|
||||||
|
val wtx = stx.tx
|
||||||
try {
|
try {
|
||||||
|
checkSignatures(stx)
|
||||||
validateDependencies(reqIdentity, wtx)
|
validateDependencies(reqIdentity, wtx)
|
||||||
checkContractValid(wtx)
|
checkContractValid(wtx)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -35,6 +38,13 @@ class ValidatingNotaryProtocol(otherSide: SingleMessageRecipient,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkSignatures(stx: SignedTransaction) {
|
||||||
|
val myKey = serviceHub.storageService.myLegalIdentity.owningKey
|
||||||
|
val missing = stx.verify(false) - myKey
|
||||||
|
|
||||||
|
if (missing.isNotEmpty()) throw NotaryException(NotaryError.SignaturesMissing(missing.toList()))
|
||||||
|
}
|
||||||
|
|
||||||
private fun checkContractValid(wtx: WireTransaction) {
|
private fun checkContractValid(wtx: WireTransaction) {
|
||||||
val ltx = wtx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments)
|
val ltx = wtx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments)
|
||||||
serviceHub.verifyTransaction(ltx)
|
serviceHub.verifyTransaction(ltx)
|
||||||
|
@ -59,7 +59,7 @@ object NotaryChangeProtocol {
|
|||||||
val me = listOf(myKey)
|
val me = listOf(myKey)
|
||||||
|
|
||||||
val signatures = if (participants == me) {
|
val signatures = if (participants == me) {
|
||||||
listOf(getNotarySignature(stx.tx))
|
listOf(getNotarySignature(stx))
|
||||||
} else {
|
} else {
|
||||||
collectSignatures(participants - me, stx)
|
collectSignatures(participants - me, stx)
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ object NotaryChangeProtocol {
|
|||||||
getParticipantSignature(participantNode, stx, sessionIdForSend)
|
getParticipantSignature(participantNode, stx, sessionIdForSend)
|
||||||
}
|
}
|
||||||
|
|
||||||
val allSignatures = participantSignatures + getNotarySignature(stx.tx)
|
val allSignatures = participantSignatures + getNotarySignature(stx)
|
||||||
sessions.forEach { send(TOPIC_CHANGE, it.key.address, it.value, allSignatures) }
|
sessions.forEach { send(TOPIC_CHANGE, it.key.address, it.value, allSignatures) }
|
||||||
|
|
||||||
return allSignatures
|
return allSignatures
|
||||||
@ -121,9 +121,9 @@ object NotaryChangeProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getNotarySignature(wtx: WireTransaction): DigitalSignature.LegallyIdentifiable {
|
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
||||||
progressTracker.currentStep = NOTARY
|
progressTracker.currentStep = NOTARY
|
||||||
return subProtocol(NotaryProtocol.Client(wtx))
|
return subProtocol(NotaryProtocol.Client(stx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.r3corda.core.contracts.TransactionType
|
|||||||
import com.r3corda.core.seconds
|
import com.r3corda.core.seconds
|
||||||
import com.r3corda.core.testing.DUMMY_NOTARY
|
import com.r3corda.core.testing.DUMMY_NOTARY
|
||||||
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
|
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
|
||||||
|
import com.r3corda.core.testing.MINI_CORP_KEY
|
||||||
import com.r3corda.node.internal.testing.MockNetwork
|
import com.r3corda.node.internal.testing.MockNetwork
|
||||||
import com.r3corda.node.internal.testing.issueState
|
import com.r3corda.node.internal.testing.issueState
|
||||||
import com.r3corda.node.services.network.NetworkMapService
|
import com.r3corda.node.services.network.NetworkMapService
|
||||||
@ -32,43 +33,53 @@ class NotaryServiceTests {
|
|||||||
keyPair = DUMMY_NOTARY_KEY,
|
keyPair = DUMMY_NOTARY_KEY,
|
||||||
advertisedServices = *arrayOf(NetworkMapService.Type, SimpleNotaryService.Type)
|
advertisedServices = *arrayOf(NetworkMapService.Type, SimpleNotaryService.Type)
|
||||||
)
|
)
|
||||||
clientNode = net.createNode(networkMapAddress = notaryNode.info)
|
clientNode = net.createNode(networkMapAddress = notaryNode.info, keyPair = MINI_CORP_KEY)
|
||||||
net.runNetwork() // Clear network map registration messages
|
net.runNetwork() // Clear network map registration messages
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should sign a unique transaction with a valid timestamp`() {
|
@Test fun `should sign a unique transaction with a valid timestamp`() {
|
||||||
|
val stx = run {
|
||||||
val inputState = issueState(clientNode)
|
val inputState = issueState(clientNode)
|
||||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
tx.setTime(Instant.now(), DUMMY_NOTARY, 30.seconds)
|
tx.setTime(Instant.now(), DUMMY_NOTARY, 30.seconds)
|
||||||
val wtx = tx.toWireTransaction()
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
val protocol = NotaryProtocol.Client(wtx)
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
|
|
||||||
val signature = future.get()
|
val signature = future.get()
|
||||||
signature.verifyWithECDSA(wtx.serialized)
|
signature.verifyWithECDSA(stx.txBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should sign a unique transaction without a timestamp`() {
|
@Test fun `should sign a unique transaction without a timestamp`() {
|
||||||
|
val stx = run {
|
||||||
val inputState = issueState(clientNode)
|
val inputState = issueState(clientNode)
|
||||||
val wtx = TransactionType.General.Builder().withItems(inputState).toWireTransaction()
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
val protocol = NotaryProtocol.Client(wtx)
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
|
|
||||||
val signature = future.get()
|
val signature = future.get()
|
||||||
signature.verifyWithECDSA(wtx.serialized)
|
signature.verifyWithECDSA(stx.txBits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should report error for transaction with an invalid timestamp`() {
|
@Test fun `should report error for transaction with an invalid timestamp`() {
|
||||||
|
val stx = run {
|
||||||
val inputState = issueState(clientNode)
|
val inputState = issueState(clientNode)
|
||||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
tx.setTime(Instant.now().plusSeconds(3600), DUMMY_NOTARY, 30.seconds)
|
tx.setTime(Instant.now().plusSeconds(3600), DUMMY_NOTARY, 30.seconds)
|
||||||
val wtx = tx.toWireTransaction()
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
val protocol = NotaryProtocol.Client(wtx)
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
|
|
||||||
@ -77,15 +88,19 @@ class NotaryServiceTests {
|
|||||||
assertTrue(error is NotaryError.TimestampInvalid)
|
assertTrue(error is NotaryError.TimestampInvalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test fun `should report error for transaction with more than one timestamp`() {
|
@Test fun `should report error for transaction with more than one timestamp`() {
|
||||||
|
val stx = run {
|
||||||
val inputState = issueState(clientNode)
|
val inputState = issueState(clientNode)
|
||||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
val timestamp = TimestampCommand(Instant.now(), 30.seconds)
|
val timestamp = TimestampCommand(Instant.now(), 30.seconds)
|
||||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||||
val wtx = tx.toWireTransaction()
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
val protocol = NotaryProtocol.Client(wtx)
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
|
|
||||||
@ -94,12 +109,17 @@ class NotaryServiceTests {
|
|||||||
assertTrue(error is NotaryError.MoreThanOneTimestamp)
|
assertTrue(error is NotaryError.MoreThanOneTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should report conflict for a duplicate transaction`() {
|
|
||||||
val inputState = issueState(clientNode)
|
|
||||||
val wtx = TransactionType.General.Builder().withItems(inputState).toWireTransaction()
|
|
||||||
|
|
||||||
val firstSpend = NotaryProtocol.Client(wtx)
|
@Test fun `should report conflict for a duplicate transaction`() {
|
||||||
val secondSpend = NotaryProtocol.Client(wtx)
|
val stx = run {
|
||||||
|
val inputState = issueState(clientNode)
|
||||||
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstSpend = NotaryProtocol.Client(stx)
|
||||||
|
val secondSpend = NotaryProtocol.Client(stx)
|
||||||
clientNode.smm.add("${NotaryProtocol.TOPIC}.first", firstSpend)
|
clientNode.smm.add("${NotaryProtocol.TOPIC}.first", firstSpend)
|
||||||
val future = clientNode.smm.add("${NotaryProtocol.TOPIC}.second", secondSpend)
|
val future = clientNode.smm.add("${NotaryProtocol.TOPIC}.second", secondSpend)
|
||||||
|
|
||||||
@ -107,7 +127,7 @@ class NotaryServiceTests {
|
|||||||
|
|
||||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||||
val notaryError = (ex.cause as NotaryException).error as NotaryError.Conflict
|
val notaryError = (ex.cause as NotaryException).error as NotaryError.Conflict
|
||||||
assertEquals(notaryError.tx, wtx)
|
assertEquals(notaryError.tx, stx.tx)
|
||||||
notaryError.conflict.verified()
|
notaryError.conflict.verified()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,26 @@
|
|||||||
package com.r3corda.node.services
|
package com.r3corda.node.services
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.Command
|
||||||
|
import com.r3corda.core.contracts.DummyContract
|
||||||
import com.r3corda.core.contracts.TransactionType
|
import com.r3corda.core.contracts.TransactionType
|
||||||
import com.r3corda.core.testing.DUMMY_NOTARY
|
import com.r3corda.core.testing.DUMMY_NOTARY
|
||||||
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
|
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
|
||||||
|
import com.r3corda.core.testing.MEGA_CORP_KEY
|
||||||
|
import com.r3corda.core.testing.MINI_CORP_KEY
|
||||||
import com.r3corda.node.internal.testing.MockNetwork
|
import com.r3corda.node.internal.testing.MockNetwork
|
||||||
import com.r3corda.node.internal.testing.issueInvalidState
|
import com.r3corda.node.internal.testing.issueInvalidState
|
||||||
|
import com.r3corda.node.internal.testing.issueState
|
||||||
import com.r3corda.node.services.network.NetworkMapService
|
import com.r3corda.node.services.network.NetworkMapService
|
||||||
import com.r3corda.node.services.transactions.ValidatingNotaryService
|
import com.r3corda.node.services.transactions.ValidatingNotaryService
|
||||||
import com.r3corda.protocols.NotaryError
|
import com.r3corda.protocols.NotaryError
|
||||||
import com.r3corda.protocols.NotaryException
|
import com.r3corda.protocols.NotaryException
|
||||||
import com.r3corda.protocols.NotaryProtocol
|
import com.r3corda.protocols.NotaryProtocol
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
class ValidatingNotaryServiceTests {
|
class ValidatingNotaryServiceTests {
|
||||||
lateinit var net: MockNetwork
|
lateinit var net: MockNetwork
|
||||||
@ -28,20 +34,47 @@ class ValidatingNotaryServiceTests {
|
|||||||
keyPair = DUMMY_NOTARY_KEY,
|
keyPair = DUMMY_NOTARY_KEY,
|
||||||
advertisedServices = *arrayOf(NetworkMapService.Type, ValidatingNotaryService.Type)
|
advertisedServices = *arrayOf(NetworkMapService.Type, ValidatingNotaryService.Type)
|
||||||
)
|
)
|
||||||
clientNode = net.createNode(networkMapAddress = notaryNode.info)
|
clientNode = net.createNode(networkMapAddress = notaryNode.info, keyPair = MINI_CORP_KEY)
|
||||||
net.runNetwork() // Clear network map registration messages
|
net.runNetwork() // Clear network map registration messages
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should report error for invalid transaction dependency`() {
|
@Test fun `should report error for invalid transaction dependency`() {
|
||||||
|
val stx = run {
|
||||||
val inputState = issueInvalidState(clientNode)
|
val inputState = issueInvalidState(clientNode)
|
||||||
val wtx = TransactionType.General.Builder().withItems(inputState).toWireTransaction()
|
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||||
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
val protocol = NotaryProtocol.Client(wtx)
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
|
|
||||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||||
val notaryError = (ex.cause as NotaryException).error
|
val notaryError = (ex.cause as NotaryException).error
|
||||||
assertTrue(notaryError is NotaryError.TransactionInvalid, "Received wrong Notary error")
|
assertThat(notaryError).isInstanceOf(NotaryError.TransactionInvalid::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun `should report error for missing signatures`() {
|
||||||
|
val expectedMissingKey = MEGA_CORP_KEY.public
|
||||||
|
val stx = run {
|
||||||
|
val inputState = issueState(clientNode)
|
||||||
|
|
||||||
|
val command = Command(DummyContract.Commands.Move(), expectedMissingKey)
|
||||||
|
val tx = TransactionType.General.Builder().withItems(inputState, command)
|
||||||
|
tx.signWith(clientNode.keyPair!!)
|
||||||
|
tx.toSignedTransaction(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val protocol = NotaryProtocol.Client(stx)
|
||||||
|
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||||
|
net.runNetwork()
|
||||||
|
|
||||||
|
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||||
|
val notaryError = (ex.cause as NotaryException).error
|
||||||
|
assertThat(notaryError).isInstanceOf(NotaryError.SignaturesMissing::class.java)
|
||||||
|
|
||||||
|
val missingKeys = (notaryError as NotaryError.SignaturesMissing).missingSigners
|
||||||
|
assertEquals(missingKeys, listOf(expectedMissingKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -353,7 +353,7 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
|
|||||||
tx.signWith(keyPair)
|
tx.signWith(keyPair)
|
||||||
|
|
||||||
// Get the notary to sign the timestamp
|
// Get the notary to sign the timestamp
|
||||||
val notarySig = subProtocol(NotaryProtocol.Client(tx.toWireTransaction()))
|
val notarySig = subProtocol(NotaryProtocol.Client(tx.toSignedTransaction(false)))
|
||||||
tx.addSignatureUnchecked(notarySig)
|
tx.addSignatureUnchecked(notarySig)
|
||||||
|
|
||||||
// Commit it to local storage.
|
// Commit it to local storage.
|
||||||
@ -368,7 +368,8 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
|
|||||||
val builder = TransactionType.General.Builder()
|
val builder = TransactionType.General.Builder()
|
||||||
CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy)
|
CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy)
|
||||||
builder.signWith(keyPair)
|
builder.signWith(keyPair)
|
||||||
builder.addSignatureUnchecked(subProtocol(NotaryProtocol.Client(builder.toWireTransaction())))
|
val notarySignature = subProtocol(NotaryProtocol.Client(builder.toSignedTransaction(false)))
|
||||||
|
builder.addSignatureUnchecked(notarySignature)
|
||||||
val tx = builder.toSignedTransaction(true)
|
val tx = builder.toSignedTransaction(true)
|
||||||
serviceHub.recordTransactions(listOf(tx))
|
serviceHub.recordTransactions(listOf(tx))
|
||||||
tx
|
tx
|
||||||
|
Loading…
x
Reference in New Issue
Block a user