mirror of
https://github.com/corda/corda.git
synced 2025-06-22 09:08:49 +00:00
Validating notary: check for missing signatures. The notary can only sign if all other signatures are present
Please enter the commit message for your changes. Lines starting
This commit is contained in:
@ -5,6 +5,7 @@ import com.r3corda.core.contracts.TransactionType
|
||||
import com.r3corda.core.seconds
|
||||
import com.r3corda.core.testing.DUMMY_NOTARY
|
||||
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.issueState
|
||||
import com.r3corda.node.services.network.NetworkMapService
|
||||
@ -32,43 +33,53 @@ class NotaryServiceTests {
|
||||
keyPair = DUMMY_NOTARY_KEY,
|
||||
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
|
||||
}
|
||||
|
||||
@Test fun `should sign a unique transaction with a valid timestamp`() {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
tx.setTime(Instant.now(), DUMMY_NOTARY, 30.seconds)
|
||||
val wtx = tx.toWireTransaction()
|
||||
val stx = run {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
tx.setTime(Instant.now(), DUMMY_NOTARY, 30.seconds)
|
||||
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)
|
||||
net.runNetwork()
|
||||
|
||||
val signature = future.get()
|
||||
signature.verifyWithECDSA(wtx.serialized)
|
||||
signature.verifyWithECDSA(stx.txBits)
|
||||
}
|
||||
|
||||
@Test fun `should sign a unique transaction without a timestamp`() {
|
||||
val inputState = issueState(clientNode)
|
||||
val wtx = TransactionType.General.Builder().withItems(inputState).toWireTransaction()
|
||||
val stx = run {
|
||||
val inputState = issueState(clientNode)
|
||||
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)
|
||||
net.runNetwork()
|
||||
|
||||
val signature = future.get()
|
||||
signature.verifyWithECDSA(wtx.serialized)
|
||||
signature.verifyWithECDSA(stx.txBits)
|
||||
}
|
||||
|
||||
@Test fun `should report error for transaction with an invalid timestamp`() {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
tx.setTime(Instant.now().plusSeconds(3600), DUMMY_NOTARY, 30.seconds)
|
||||
val wtx = tx.toWireTransaction()
|
||||
val stx = run {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
tx.setTime(Instant.now().plusSeconds(3600), DUMMY_NOTARY, 30.seconds)
|
||||
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)
|
||||
net.runNetwork()
|
||||
|
||||
@ -77,15 +88,19 @@ class NotaryServiceTests {
|
||||
assertTrue(error is NotaryError.TimestampInvalid)
|
||||
}
|
||||
|
||||
@Test fun `should report error for transaction with more than one timestamp`() {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
val timestamp = TimestampCommand(Instant.now(), 30.seconds)
|
||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||
val wtx = tx.toWireTransaction()
|
||||
|
||||
val protocol = NotaryProtocol.Client(wtx)
|
||||
@Test fun `should report error for transaction with more than one timestamp`() {
|
||||
val stx = run {
|
||||
val inputState = issueState(clientNode)
|
||||
val tx = TransactionType.General.Builder().withItems(inputState)
|
||||
val timestamp = TimestampCommand(Instant.now(), 30.seconds)
|
||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||
tx.addCommand(timestamp, DUMMY_NOTARY.owningKey)
|
||||
tx.signWith(clientNode.keyPair!!)
|
||||
tx.toSignedTransaction(false)
|
||||
}
|
||||
|
||||
val protocol = NotaryProtocol.Client(stx)
|
||||
val future = clientNode.smm.add(NotaryProtocol.TOPIC, protocol)
|
||||
net.runNetwork()
|
||||
|
||||
@ -94,12 +109,17 @@ class NotaryServiceTests {
|
||||
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)
|
||||
val secondSpend = NotaryProtocol.Client(wtx)
|
||||
@Test fun `should report conflict for a duplicate transaction`() {
|
||||
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)
|
||||
val future = clientNode.smm.add("${NotaryProtocol.TOPIC}.second", secondSpend)
|
||||
|
||||
@ -107,7 +127,7 @@ class NotaryServiceTests {
|
||||
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||
val notaryError = (ex.cause as NotaryException).error as NotaryError.Conflict
|
||||
assertEquals(notaryError.tx, wtx)
|
||||
assertEquals(notaryError.tx, stx.tx)
|
||||
notaryError.conflict.verified()
|
||||
}
|
||||
}
|
@ -1,20 +1,26 @@
|
||||
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.testing.DUMMY_NOTARY
|
||||
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.issueInvalidState
|
||||
import com.r3corda.node.internal.testing.issueState
|
||||
import com.r3corda.node.services.network.NetworkMapService
|
||||
import com.r3corda.node.services.transactions.ValidatingNotaryService
|
||||
import com.r3corda.protocols.NotaryError
|
||||
import com.r3corda.protocols.NotaryException
|
||||
import com.r3corda.protocols.NotaryProtocol
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.ExecutionException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ValidatingNotaryServiceTests {
|
||||
lateinit var net: MockNetwork
|
||||
@ -28,20 +34,47 @@ class ValidatingNotaryServiceTests {
|
||||
keyPair = DUMMY_NOTARY_KEY,
|
||||
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
|
||||
}
|
||||
|
||||
@Test fun `should report error for invalid transaction dependency`() {
|
||||
val inputState = issueInvalidState(clientNode)
|
||||
val wtx = TransactionType.General.Builder().withItems(inputState).toWireTransaction()
|
||||
val stx = run {
|
||||
val inputState = issueInvalidState(clientNode)
|
||||
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)
|
||||
net.runNetwork()
|
||||
|
||||
val ex = assertFailsWith(ExecutionException::class) { future.get() }
|
||||
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))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user