mirror of
https://github.com/corda/corda.git
synced 2025-05-02 16:53:22 +00:00
Make notary flow return a collection of signatures to support the BFT… (#264)
Make notary flow return a collection of signatures to support the BFT notary. For a single-node or RAFT notary it would just contain a single signature.
This commit is contained in:
parent
c7abbe8791
commit
006faa82a1
@ -63,7 +63,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
val me = listOf(myKey)
|
||||
|
||||
val signatures = if (participants == me) {
|
||||
listOf(getNotarySignature(stx))
|
||||
getNotarySignatures(stx)
|
||||
} else {
|
||||
collectSignatures(participants - me, stx)
|
||||
}
|
||||
@ -87,7 +87,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
|
||||
val allPartySignedTx = stx + participantSignatures
|
||||
|
||||
val allSignatures = participantSignatures + getNotarySignature(allPartySignedTx)
|
||||
val allSignatures = participantSignatures + getNotarySignatures(allPartySignedTx)
|
||||
parties.forEach { send(it, allSignatures) }
|
||||
|
||||
return allSignatures
|
||||
@ -105,7 +105,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
|
||||
private fun getNotarySignatures(stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
||||
progressTracker.currentStep = NOTARY
|
||||
try {
|
||||
return subFlow(NotaryFlow.Client(stx))
|
||||
|
@ -74,8 +74,8 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
|
||||
return stxnsAndParties.map { pair ->
|
||||
val stx = pair.first
|
||||
val notarised = if (needsNotarySignature(stx)) {
|
||||
val notarySig = subFlow(NotaryFlow.Client(stx))
|
||||
stx + notarySig
|
||||
val notarySignatures = subFlow(NotaryFlow.Client(stx))
|
||||
stx + notarySignatures
|
||||
} else {
|
||||
stx
|
||||
}
|
||||
@ -122,7 +122,7 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
|
||||
// Load and verify each transaction.
|
||||
return sorted.map { stx ->
|
||||
val notary = stx.tx.notary
|
||||
// The notary signature is allowed to be missing but no others.
|
||||
// The notary signature(s) are allowed to be missing but no others.
|
||||
val wtx = if (notary != null) stx.verifySignatures(notary.owningKey) else stx.verifySignatures()
|
||||
val ltx = wtx.toLedgerTransaction(augmentedLookup)
|
||||
ltx.verify()
|
||||
|
@ -16,14 +16,17 @@ import net.corda.core.utilities.unwrap
|
||||
|
||||
object NotaryFlow {
|
||||
/**
|
||||
* A flow to be used by a party for obtaining a signature from a [NotaryService] ascertaining the transaction
|
||||
* A flow to be used by a party for obtaining signature(s) from a [NotaryService] ascertaining the transaction
|
||||
* timestamp is correct and none of its inputs have been used in another completed transaction.
|
||||
*
|
||||
* In case of a single-node or Raft notary, the flow will return a single signature. For the BFT notary multiple
|
||||
* signatures will be returned – one from each replica that accepted the input state commit.
|
||||
*
|
||||
* @throws NotaryException in case the any of the inputs to the transaction have been consumed
|
||||
* by another transaction or the timestamp is invalid.
|
||||
*/
|
||||
open class Client(private val stx: SignedTransaction,
|
||||
override val progressTracker: ProgressTracker) : FlowLogic<DigitalSignature.WithKey>() {
|
||||
override val progressTracker: ProgressTracker) : FlowLogic<List<DigitalSignature.WithKey>>() {
|
||||
constructor(stx: SignedTransaction) : this(stx, Client.tracker())
|
||||
|
||||
companion object {
|
||||
@ -37,7 +40,7 @@ object NotaryFlow {
|
||||
|
||||
@Suspendable
|
||||
@Throws(NotaryException::class)
|
||||
override fun call(): DigitalSignature.WithKey {
|
||||
override fun call(): List<DigitalSignature.WithKey> {
|
||||
progressTracker.currentStep = REQUESTING
|
||||
val wtx = stx.tx
|
||||
notaryParty = wtx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||
@ -57,7 +60,7 @@ object NotaryFlow {
|
||||
}
|
||||
|
||||
val response = try {
|
||||
sendAndReceive<DigitalSignature.WithKey>(notaryParty, payload)
|
||||
sendAndReceive<List<DigitalSignature.WithKey>>(notaryParty, payload)
|
||||
} catch (e: NotaryException) {
|
||||
if (e.error is NotaryError.Conflict) {
|
||||
e.error.conflict.verified()
|
||||
@ -65,9 +68,9 @@ object NotaryFlow {
|
||||
throw e
|
||||
}
|
||||
|
||||
return response.unwrap { sig ->
|
||||
validateSignature(sig, stx.id.bytes)
|
||||
sig
|
||||
return response.unwrap { signatures ->
|
||||
signatures.forEach { validateSignature(it, stx.id.bytes) }
|
||||
signatures
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,8 +116,8 @@ object NotaryFlow {
|
||||
|
||||
@Suspendable
|
||||
private fun signAndSendResponse(txId: SecureHash) {
|
||||
val sig = sign(txId.bytes)
|
||||
send(otherSide, sig)
|
||||
val signature = sign(txId.bytes)
|
||||
send(otherSide, listOf(signature))
|
||||
}
|
||||
|
||||
private fun validateTimestamp(t: Timestamp?) {
|
||||
|
@ -42,7 +42,7 @@ object TwoPartyDealFlow {
|
||||
// This object is serialised to the network and is the first flow message the seller sends to the buyer.
|
||||
data class Handshake<out T>(val payload: T, val publicKey: CompositeKey)
|
||||
|
||||
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.WithKey)
|
||||
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySigs: List<DigitalSignature.WithKey>)
|
||||
|
||||
/**
|
||||
* [Primary] at the end sends the signed tx to all the regulator parties. This a seperate workflow which needs a
|
||||
@ -139,9 +139,9 @@ object TwoPartyDealFlow {
|
||||
// These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.
|
||||
val ourSignature = computeOurSignature(stx)
|
||||
val allPartySignedTx = stx + ourSignature
|
||||
val notarySignature = getNotarySignature(allPartySignedTx)
|
||||
val notarySignatures = getNotarySignatures(allPartySignedTx)
|
||||
|
||||
val fullySigned = sendSignatures(allPartySignedTx, ourSignature, notarySignature)
|
||||
val fullySigned = sendSignatures(allPartySignedTx, ourSignature, notarySignatures)
|
||||
|
||||
progressTracker.currentStep = RECORDING
|
||||
|
||||
@ -161,7 +161,7 @@ object TwoPartyDealFlow {
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
|
||||
private fun getNotarySignatures(stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
||||
progressTracker.currentStep = NOTARY
|
||||
return subFlow(NotaryFlow.Client(stx))
|
||||
}
|
||||
@ -173,13 +173,13 @@ object TwoPartyDealFlow {
|
||||
|
||||
@Suspendable
|
||||
private fun sendSignatures(allPartySignedTx: SignedTransaction, ourSignature: DigitalSignature.WithKey,
|
||||
notarySignature: DigitalSignature.WithKey): SignedTransaction {
|
||||
notarySignatures: List<DigitalSignature.WithKey>): SignedTransaction {
|
||||
progressTracker.currentStep = SENDING_SIGS
|
||||
val fullySigned = allPartySignedTx + notarySignature
|
||||
val fullySigned = allPartySignedTx + notarySignatures
|
||||
|
||||
logger.trace { "Built finished transaction, sending back to other party!" }
|
||||
|
||||
send(otherParty, SignaturesFromPrimary(ourSignature, notarySignature))
|
||||
send(otherParty, SignaturesFromPrimary(ourSignature, notarySignatures))
|
||||
return fullySigned
|
||||
}
|
||||
}
|
||||
@ -217,7 +217,7 @@ object TwoPartyDealFlow {
|
||||
|
||||
logger.trace { "Got signatures from other party, verifying ... " }
|
||||
|
||||
val fullySigned = stx + signatures.sellerSig + signatures.notarySig
|
||||
val fullySigned = stx + signatures.sellerSig + signatures.notarySigs
|
||||
fullySigned.verifySignatures()
|
||||
|
||||
logger.trace { "Signatures received are valid. Deal transaction complete! :-)" }
|
||||
|
@ -55,8 +55,8 @@ class NotaryServiceTests {
|
||||
}
|
||||
|
||||
val future = runNotaryClient(stx)
|
||||
val signature = future.getOrThrow()
|
||||
signature.verifyWithECDSA(stx.id)
|
||||
val signatures = future.getOrThrow()
|
||||
signatures.forEach { it.verifyWithECDSA(stx.id) }
|
||||
}
|
||||
|
||||
@Test fun `should sign a unique transaction without a timestamp`() {
|
||||
@ -68,8 +68,8 @@ class NotaryServiceTests {
|
||||
}
|
||||
|
||||
val future = runNotaryClient(stx)
|
||||
val signature = future.getOrThrow()
|
||||
signature.verifyWithECDSA(stx.id)
|
||||
val signatures = future.getOrThrow()
|
||||
signatures.forEach { it.verifyWithECDSA(stx.id) }
|
||||
}
|
||||
|
||||
@Test fun `should report error for transaction with an invalid timestamp`() {
|
||||
@ -132,7 +132,7 @@ class NotaryServiceTests {
|
||||
notaryError.conflict.verified()
|
||||
}
|
||||
|
||||
private fun runNotaryClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
|
||||
private fun runNotaryClient(stx: SignedTransaction): ListenableFuture<List<DigitalSignature.WithKey>> {
|
||||
val flow = NotaryFlow.Client(stx)
|
||||
val future = clientNode.services.startFlow(flow).resultFuture
|
||||
net.runNetwork()
|
||||
|
@ -78,7 +78,7 @@ class ValidatingNotaryServiceTests {
|
||||
assertEquals(setOf(expectedMissingKey), missingKeys)
|
||||
}
|
||||
|
||||
private fun runClient(stx: SignedTransaction): ListenableFuture<DigitalSignature.WithKey> {
|
||||
private fun runClient(stx: SignedTransaction): ListenableFuture<List<DigitalSignature.WithKey>> {
|
||||
val flow = NotaryFlow.Client(stx)
|
||||
val future = clientNode.services.startFlow(flow).resultFuture
|
||||
net.runNetwork()
|
||||
|
@ -78,7 +78,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) {
|
||||
*/
|
||||
private fun notariseTransactions(transactions: List<SignedTransaction>): List<String> {
|
||||
val signatureFutures = transactions.map { rpc.startFlow(NotaryFlow::Client, it).returnValue }
|
||||
return Futures.allAsList(signatureFutures).getOrThrow().map { it.by.toStringShort() }
|
||||
return Futures.allAsList(signatureFutures).getOrThrow().map { it.map { it.by.toStringShort() }.joinToString() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,8 +80,8 @@ class SellerFlow(val otherParty: Party,
|
||||
tx.signWith(keyPair)
|
||||
|
||||
// Get the notary to sign the timestamp
|
||||
val notarySig = subFlow(NotaryFlow.Client(tx.toSignedTransaction(false)))
|
||||
tx.addSignatureUnchecked(notarySig)
|
||||
val notarySigs = subFlow(NotaryFlow.Client(tx.toSignedTransaction(false)))
|
||||
notarySigs.forEach { tx.addSignatureUnchecked(it) }
|
||||
|
||||
// Commit it to local storage.
|
||||
val stx = tx.toSignedTransaction(true)
|
||||
@ -96,7 +96,7 @@ class SellerFlow(val otherParty: Party,
|
||||
CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy)
|
||||
builder.signWith(keyPair)
|
||||
val notarySignature = subFlow(NotaryFlow.Client(builder.toSignedTransaction(false)))
|
||||
builder.addSignatureUnchecked(notarySignature)
|
||||
notarySignature.forEach { builder.addSignatureUnchecked(it) }
|
||||
val tx = builder.toSignedTransaction(true)
|
||||
serviceHub.recordTransactions(listOf(tx))
|
||||
tx
|
||||
|
Loading…
x
Reference in New Issue
Block a user