Test Contract.verify() using cryptographic verify function.

This commit is contained in:
Chris Rankin 2019-10-14 18:22:44 +01:00
parent 01e235f9d3
commit ceeb170f6c
3 changed files with 139 additions and 0 deletions

View File

@ -0,0 +1,39 @@
package net.corda.contracts.djvm.crypto
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.crypto.Crypto
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.OpaqueBytes
import java.security.PublicKey
class DeterministicCryptoContract : Contract {
override fun verify(tx: LedgerTransaction) {
val cryptoData = tx.outputsOfType(CryptoState::class.java)
val validators = tx.commandsOfType(Validate::class.java)
val isValid = validators.all { validate ->
with (validate.value) {
cryptoData.all { crypto ->
Crypto.doVerify(schemeCodeName, publicKey, crypto.signature.bytes, crypto.original.bytes)
}
}
}
if (cryptoData.isEmpty() || validators.isEmpty() || !isValid) {
throw IllegalStateException("Failed to validate signatures in command data")
}
}
@Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
class CryptoState(val owner: AbstractParty, val original: OpaqueBytes, val signature: OpaqueBytes) : ContractState {
override val participants: List<AbstractParty> = listOf(owner)
}
class Validate(
val schemeCodeName: String,
val publicKey: PublicKey
) : CommandData
}

View File

@ -0,0 +1,32 @@
package net.corda.flows.djvm.crypto
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.djvm.crypto.DeterministicCryptoContract
import net.corda.core.contracts.Command
import net.corda.core.contracts.CommandData
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
@InitiatingFlow
@StartableByRPC
class DeterministicCryptoFlow(
private val command: CommandData,
private val original: OpaqueBytes,
private val signature: OpaqueBytes
) : FlowLogic<SecureHash>() {
@Suspendable
override fun call(): SecureHash {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val stx = serviceHub.signInitialTransaction(
TransactionBuilder(notary)
.addOutputState(DeterministicCryptoContract.CryptoState(ourIdentity, original, signature))
.addCommand(Command(command, ourIdentity.owningKey))
)
stx.verify(serviceHub, checkSufficientSignatures = false)
return stx.id
}
}

View File

@ -0,0 +1,68 @@
package net.corda.node.services
import net.corda.contracts.djvm.crypto.DeterministicCryptoContract.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.Crypto.DEFAULT_SIGNATURE_SCHEME
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.flows.djvm.crypto.DeterministicCryptoFlow
import net.corda.node.DeterministicSourcesRule
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.internal.cordappsForPackages
import org.junit.ClassRule
import org.junit.Test
import org.junit.jupiter.api.assertDoesNotThrow
import java.security.KeyPairGenerator
class DeterministicContractCryptoTest {
companion object {
const val MESSAGE = "Very Important Data! Do Not Change!"
val logger = loggerFor<NonDeterministicContractVerifyTest>()
@ClassRule
@JvmField
val djvmSources = DeterministicSourcesRule()
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
return DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = cordappsForPackages(
"net.corda.contracts.djvm.crypto",
"net.corda.flows.djvm.crypto"
),
djvmBootstrapSource = djvmSources.bootstrap,
djvmCordaSource = djvmSources.corda
)
}
}
@Test
fun `test DJVM can verify using crypto`() {
val keyPair = KeyPairGenerator.getInstance(DEFAULT_SIGNATURE_SCHEME.algorithmName).genKeyPair()
val importantData = OpaqueBytes(MESSAGE.toByteArray())
val signature = OpaqueBytes(Crypto.doSign(DEFAULT_SIGNATURE_SCHEME, keyPair.`private`, importantData.bytes))
val validate = Validate(
schemeCodeName = DEFAULT_SIGNATURE_SCHEME.schemeCodeName,
publicKey = keyPair.`public`
)
driver(parametersFor(djvmSources)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val txId = assertDoesNotThrow {
alice.rpc.startFlow(::DeterministicCryptoFlow, validate, importantData, signature)
.returnValue.getOrThrow()
}
logger.info("TX-ID: {}", txId)
}
}
}