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:
Andrius Dagys
2016-06-20 18:29:48 +01:00
parent 388c26dd35
commit aaeb99ac25
8 changed files with 122 additions and 55 deletions

View File

@ -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()
}
}

View File

@ -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))
}
}