mirror of
https://github.com/corda/corda.git
synced 2025-02-05 02:29:20 +00:00
Test Contract.verify() using cryptographic verify function.
This commit is contained in:
parent
01e235f9d3
commit
ceeb170f6c
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user