mirror of
https://github.com/corda/corda.git
synced 2025-01-22 12:28:11 +00:00
Add transaction verification
This commit is contained in:
parent
633be8d631
commit
b4946db9ef
@ -5,3 +5,5 @@ package com.r3.conclave.cordapp.common
|
||||
* attestation contained in the mail body (requires an enclave host to be registered).
|
||||
*/
|
||||
class InitPostOfficeToRemoteEnclave : EnclaveCommand
|
||||
|
||||
class VerifyUnencryptedTx : EnclaveCommand
|
@ -0,0 +1,21 @@
|
||||
package com.r3.conclave.cordapp.common.dto
|
||||
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.security.PublicKey
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@CordaSerializable
|
||||
data class ConclaveNetworkParameters(
|
||||
val minimumPlatformVersion: Int,
|
||||
val notaries: Array<NotaryInfo>,
|
||||
val maxMessageSize: Int,
|
||||
val maxTransactionSize: Int,
|
||||
val modifiedTime: Instant,
|
||||
val epoch: Int,
|
||||
val whitelistedContractImplementations: Array<Pair<String, Array<AttachmentId>>>,
|
||||
val eventHorizon: Duration,
|
||||
val packageOwnership: Array<Pair<String, PublicKey>>
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package com.r3.conclave.cordapp.common.dto
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
@CordaSerializable
|
||||
data class ConclaveVerificationResponse(
|
||||
val verificationStatus: VerificationStatus,
|
||||
val verificationErrorMessage: String?
|
||||
)
|
@ -0,0 +1,8 @@
|
||||
package com.r3.conclave.cordapp.common.dto
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
@CordaSerializable
|
||||
enum class VerificationStatus {
|
||||
SUCCESS, VERIFICATION_ERROR
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.r3.conclave.cordapp.common.dto
|
||||
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
|
||||
@CordaSerializable
|
||||
data class WireTxAdditionalInfo(
|
||||
val wireTransaction: WireTransaction,
|
||||
val inputStates: Array<StateAndRef<ContractState>>,
|
||||
val attachments: Array<Attachment>,
|
||||
val conclaveNetworkParameters: ConclaveNetworkParameters,
|
||||
val references: Array<StateAndRef<ContractState>>,
|
||||
val serializedInputs: Array<ByteArray>?,
|
||||
val serializedReferences: Array<ByteArray>?
|
||||
)
|
@ -1,10 +1,22 @@
|
||||
package com.r3.conclave.cordapp.sample.enclave
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.r3.conclave.common.EnclaveInstanceInfo
|
||||
import com.r3.conclave.cordapp.common.*
|
||||
import com.r3.conclave.cordapp.common.dto.ConclaveVerificationResponse
|
||||
import com.r3.conclave.cordapp.common.dto.VerificationStatus
|
||||
import com.r3.conclave.cordapp.common.dto.WireTxAdditionalInfo
|
||||
import com.r3.conclave.enclave.EnclavePostOffice
|
||||
import com.r3.conclave.mail.EnclaveMail
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
|
||||
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
|
||||
import net.corda.serialization.internal.SerializationFactoryImpl
|
||||
import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey
|
||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import java.lang.IllegalStateException
|
||||
import java.security.Security
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@ -15,9 +27,29 @@ class EncryptedTxEnclave : CommandEnclave() {
|
||||
private lateinit var remoteEnclavePostOffice: EnclavePostOffice
|
||||
|
||||
private val commandToCallableMap = mapOf(
|
||||
InitPostOfficeToRemoteEnclave::class.java.name to ::initPostOfficeToRemoteEnclave
|
||||
InitPostOfficeToRemoteEnclave::class.java.name to ::initPostOfficeToRemoteEnclave,
|
||||
VerifyUnencryptedTx::class.java.name to ::verifyUnencryptedTransaction
|
||||
)
|
||||
|
||||
private val serializerFactoriesForContexts = Caffeine.newBuilder()
|
||||
.maximumSize(128)
|
||||
.build<SerializationFactoryCacheKey, SerializerFactory>()
|
||||
.asMap()
|
||||
|
||||
private var serializationFactoryImpl: SerializationFactoryImpl
|
||||
|
||||
init {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
|
||||
val serverScheme = AMQPServerSerializationScheme(emptyList(), serializerFactoriesForContexts)
|
||||
val clientScheme = AMQPServerSerializationScheme(emptyList(), serializerFactoriesForContexts)
|
||||
|
||||
serializationFactoryImpl = SerializationFactoryImpl().apply {
|
||||
registerScheme(serverScheme)
|
||||
registerScheme(clientScheme)
|
||||
}
|
||||
}
|
||||
|
||||
override fun receiveMail(mail: EnclaveMail, flowId: String, route: EnclaveCommand) {
|
||||
if (commandToCallableMap[route.javaClass.name] != null) {
|
||||
commandToCallableMap[route.javaClass.name]!!.invoke(mail, flowId)
|
||||
@ -47,4 +79,39 @@ class EncryptedTxEnclave : CommandEnclave() {
|
||||
|
||||
remoteEnclavePostOffice = postOffice(attestation, flowId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the serialized [WireTxAdditionalInfo] in the message body to a [LedgerTransaction] and verify.
|
||||
* This command replies with a serialized [ConclaveVerificationResponse] indicating if the verification was a
|
||||
* success or threw an error.
|
||||
* @param mail an incoming [EnclaveMail] containing a serialized [WireTxAdditionalInfo].
|
||||
* @param flowId the ID of the flow executing on our host.
|
||||
*/
|
||||
private fun verifyUnencryptedTransaction(mail: EnclaveMail, flowId: String) {
|
||||
val txBody = mail.bodyAsBytes
|
||||
|
||||
val wireTx = serializationFactoryImpl.deserialize(
|
||||
byteSequence = ByteSequence.of(txBody, 0, txBody.size),
|
||||
clazz = WireTxAdditionalInfo::class.java,
|
||||
context = AMQP_P2P_CONTEXT)
|
||||
|
||||
val ledgerTx = LedgerTxHelper.toLedgerTxInternal(wireTx)
|
||||
|
||||
val response = serializationFactoryImpl.asCurrent {
|
||||
this.withCurrentContext(AMQP_P2P_CONTEXT) {
|
||||
try {
|
||||
ledgerTx.verify()
|
||||
ConclaveVerificationResponse(VerificationStatus.SUCCESS, null)
|
||||
} catch (e: Exception) {
|
||||
println("Exception while verifying transaction $e")
|
||||
ConclaveVerificationResponse(VerificationStatus.VERIFICATION_ERROR, e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val serializedResponse = serializationFactoryImpl.serialize(response, AMQP_P2P_CONTEXT).bytes
|
||||
|
||||
val responseBytes = postOffice(mail).encryptMail(serializedResponse)
|
||||
postMail(responseBytes, "$flowId:${VerifyUnencryptedTx().serialize()}")
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.r3.conclave.cordapp.sample.enclave
|
||||
|
||||
import com.r3.conclave.cordapp.common.dto.ConclaveNetworkParameters
|
||||
import com.r3.conclave.cordapp.common.dto.WireTxAdditionalInfo
|
||||
import net.corda.core.contracts.CommandWithParties
|
||||
import net.corda.core.internal.SerializedStateAndRef
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
|
||||
class LedgerTxHelper {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun toLedgerTxInternal(conclaveLedgerTxModel: WireTxAdditionalInfo): LedgerTransaction {
|
||||
|
||||
val serializedResolvedInputs = conclaveLedgerTxModel.serializedInputs?.mapIndexed { index, ref ->
|
||||
SerializedStateAndRef(SerializedBytes(ref), conclaveLedgerTxModel
|
||||
.inputStates[index].ref)
|
||||
}
|
||||
|
||||
val serializedResolvedReferences = conclaveLedgerTxModel.serializedReferences?.mapIndexed { index, ref ->
|
||||
SerializedStateAndRef(SerializedBytes(ref), conclaveLedgerTxModel.references[index].ref)
|
||||
}
|
||||
|
||||
return LedgerTransaction.createForConclaveVerify(
|
||||
conclaveLedgerTxModel.inputStates.toList(),
|
||||
conclaveLedgerTxModel.wireTransaction.outputs,
|
||||
conclaveLedgerTxModel.wireTransaction.commands.map {
|
||||
CommandWithParties(it.signers, emptyList(), it.value)
|
||||
},
|
||||
conclaveLedgerTxModel.attachments.toList(),
|
||||
conclaveLedgerTxModel.wireTransaction.id,
|
||||
conclaveLedgerTxModel.wireTransaction.notary,
|
||||
conclaveLedgerTxModel.wireTransaction.timeWindow,
|
||||
conclaveLedgerTxModel.wireTransaction.privacySalt,
|
||||
toNetworkParameters(conclaveLedgerTxModel.conclaveNetworkParameters),
|
||||
conclaveLedgerTxModel.references.toList(),
|
||||
conclaveLedgerTxModel.wireTransaction.componentGroups,
|
||||
serializedResolvedInputs,
|
||||
serializedResolvedReferences,
|
||||
conclaveLedgerTxModel.wireTransaction.digestService
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toNetworkParameters(conclaveNetworkParameters: ConclaveNetworkParameters): NetworkParameters {
|
||||
|
||||
return NetworkParameters(
|
||||
conclaveNetworkParameters.minimumPlatformVersion,
|
||||
conclaveNetworkParameters.notaries.toList(),
|
||||
conclaveNetworkParameters.maxMessageSize,
|
||||
conclaveNetworkParameters.maxTransactionSize,
|
||||
conclaveNetworkParameters.modifiedTime,
|
||||
conclaveNetworkParameters.epoch,
|
||||
conclaveNetworkParameters.whitelistedContractImplementations.map {
|
||||
it.first to it.second.asList()
|
||||
}.toMap(),
|
||||
conclaveNetworkParameters.eventHorizon,
|
||||
conclaveNetworkParameters.packageOwnership.toMap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user