Broadcasting in Corda 7 - Missing signature


(Srinidhi) #1

Hi,

Trying to broadcast an updated state from PartyA to PartyB and PartyC as per this below flow diagram.

Broadcast flow Code -

object BroadcastFlow{

class Initiator(val myState: MyState) : FlowLogic<BroadcastFlowResult>() {
    @Suspendable
    override fun call(): BroadcastFlowResult {
        try {

            val myKeyPair = serviceHub.legalIdentityKey
            // Obtain a reference to the notary we want to use and its public key.
            val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
            //  progressTracker!!.currentStep = CONSTRUCTING_OFFER

            val index = serviceHub.vaultService.linearHeadsOfType<MyState>().values.indexOfFirst { it.state.data.model.reqNo==myState.model.reqNo }
            val oldState = serviceHub.vaultService.linearHeadsOfType<MyState>().values.elementAt(index)

            val newState = oldState.state.data.copy(status= "UPDATED")

            //Generate a Txn
            val unsignedTxn = TransactionType.General.Builder(notary).
                    withItems(oldState,newState,Command(MyContract.Commands.Update(),listOf(myKeyPair.public.composite,nodeB.owningKey,nodeC.owningKey)))
            val currentTime = serviceHub.clock.instant()
            unsignedTxn.setTime(currentTime, 30.seconds)
            //Sign Txn
            val partiallySignedTxnPartyA = unsignedTxn.signWith(myKeyPair).toSignedTransaction(checkSufficientSignatures = false)

            //Send Partially signed Txn to PartyB. Get its signature, sign it and verify to get the fully signed Txn.
            val fullySignedTxnFromPartyB = sendAndReceive<DigitalSignature.WithKey>(newState.partyB,partiallySignedTxnPartyA).unwrap {

                val withPartyBsign = partiallySignedTxnPartyA + it
                // check all signatures are present except the notary
                withPartyBsign.verifySignatures(withPartyBsign.tx.notary!!.owningKey)
                withPartyBsign.toLedgerTransaction(serviceHub).verify() // Here it is checking for notary's sign and getting missing signature exception.
                withPartyBsign
            }
            //Send Partially signed Txn to PartyC. Get its signature, sign it and verify to get the fully signed Txn.
            val fullySignedTxnFromPartyC = sendAndReceive<DigitalSignature.WithKey>(newState.partyC,fullySignedTxnFromPartyB).unwrap {

                val withPartyCsign = fullySignedTxnFromPartyB + it
                // check all signatures are present except the notary
                withPartyCsign.verifySignatures(withPartyCsign.tx.notary!!.owningKey)
                withPartyCsign.toLedgerTransaction(serviceHub).verify()
                withPartyCsign
            }
            //Broadcast it to PartyB and PartyC.
            subFlow(FinalityFlow(fullySignedTxnFromPartyC , setOf(serviceHub.myInfo.legalIdentity,nodeB.owningKey,nodeC.owningKey)))
            return BroadcastFlowResult.Success("Transaction ${fullySignedTxnFromNodeC.id} is Broadcasted across Node B and Node C")
        } catch(ex: Exception) {
            // Just catch all exception types.
            return BroadcastFlowResult.Failure(ex.message)
        }
    }
}

class Acceptor(val otherParty: Party): FlowLogic<BroadcastFlowResult>() {

    @Suspendable
    override fun call(): BroadcastFlowResult {
        try {
            // Obtain a reference to our key pair.
            val keyPair = serviceHub.legalIdentityKey
            val ptx = receive<SignedTransaction>(otherParty).unwrap {
                val wtx = it.tx
                // check all signatures are present except our own and the notary
                it.verifySignatures(keyPair.public.composite, wtx.notary!!.owningKey)
                it // return the SignedTransaction
            }
            val fullSignature = serviceHub.legalIdentityKey.signWithECDSA(ptx.id)
            send(otherParty,fullSignature)
            return BroadcastFlowResult.Success("Signed and Sent back !!")
        }
        catch (ex: Exception) {
            return BroadcastFlowResult.Failure(ex.message)
        }
    }
}

I’m getting an exception when the control comes to - toLedgerTransaction(serviceHub).verify in the Intitiator
which says - Missing Signature for "a public key"

Any clue ?

Thanks.


(Joel Dudley) #2

Hi Srinidhi,

Are you sure it’s not throwing an error on the previous line, where you call verifySignatures()?

Looking at your call to verifySignatures(), it looks like you aren’t passing in all the keys that are allowed to be missing. For example, it looks like node C hasn’t signed at this point, and yet you do not include it in the allowedToBeMissing vararg.


(Srinidhi) #3

Hello @joeldudley,

Its not throwing any error on the previous line.

If i comment out - toLedgerTransaction(serviceHub).verify() its broadcasting to other nodes. But i guess if i do so my contract verification will be skipped.


(Joel Dudley) #4

@srinidhi Can you check whether it’s the call to toLedgerTransaction() or the subsequent call to verify() that’s throwing this error?

Can you try attaching a debugger to your node via IntelliJ by following the instructions here: https://docs.corda.net/CLI-vs-IDE.html#debugging, and finding out more about what causes the exception to throw?


(Srinidhi) #5

@joeldudley,

Here is the stack trace.

1. Below snapshot gives composite keys for all the parties.

2. Exception in verifySignatures() in SignedTransaction Class.

3. Trace for the above exception.

Looks like PartyC’s Sign is missing. Where can i include this signature ?

Thanks.


(Mike Hearn) #6

Hi Srinidhi, you didn’t include the stack trace in your response or line numbers for the code, so it’s hard to know where the actual problem is.

I think what Joel said is correct - you’re checking a transaction that is expected to be missing party C’s signatures (because they didn’t sign yet!) but you aren’t telling the system that. On the line:

withPartyBsign.verifySignatures(withPartyBsign.tx.notary!!.owningKey)

you should probably pass party C’s key as well, right, because at that point you expect the transaction to be missing both the notary’s key and party C’s key.


(Srinidhi) #7

Hello @mike, @joeldudley

Have included PartyC and Notary composite Keys (Just not to verify for their signatures at this point) in the line
withPartyBsign.verifySignatures(withPartyBsign.tx.notary!!.owningKey, myState.partyC!!.owningKey) it doesnt complain anything.
But when it executes the next line i.e withPartyBsign.toLedgerTransaction(serviceHub).verify() which will call verifySignatures() as below (Part of SignedTransaction Class)

fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)

Here it is expecting PartyC’s Sign as needed=1.

Same when i try having only Party A and Party B(Excluding PartyC) as participants of the Transaction. while creating a transaction, It is expecting Notary’s Signature as needed=1.

Both happens to be from
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)


(Srinidhi) #8

Hi @mike @joeldudley,

Have identified the issue. I was making a wrong call to toLedgerTransaction(…) which will again verify the signature. Used WireTransaction version to fix it.

Thank you.


(Joel Dudley) #9

No problem - good to see it fixed.