mirror of
https://github.com/corda/corda.git
synced 2024-12-24 23:26:48 +00:00
Deserialise LedgerTransaction into the sandbox for Contract.verify().
This commit is contained in:
parent
5666b2406a
commit
0b3b1a9884
@ -488,6 +488,7 @@ bintrayConfig {
|
|||||||
'corda-finance-contracts',
|
'corda-finance-contracts',
|
||||||
'corda-node',
|
'corda-node',
|
||||||
'corda-node-api',
|
'corda-node-api',
|
||||||
|
'corda-node-djvm',
|
||||||
'corda-test-common',
|
'corda-test-common',
|
||||||
'corda-test-utils',
|
'corda-test-utils',
|
||||||
'corda-test-db',
|
'corda-test-db',
|
||||||
|
@ -8,7 +8,6 @@ import net.corda.core.crypto.*
|
|||||||
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.accessLeafIndex
|
|
||||||
import net.corda.core.node.NotaryInfo
|
import net.corda.core.node.NotaryInfo
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
@ -307,37 +306,37 @@ class PartialMerkleTreeTest {
|
|||||||
|
|
||||||
val pmt = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("1"), SecureHash.sha256("5"), SecureHash.sha256("0"), SecureHash.sha256("19")))
|
val pmt = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("1"), SecureHash.sha256("5"), SecureHash.sha256("0"), SecureHash.sha256("19")))
|
||||||
// First leaf.
|
// First leaf.
|
||||||
assertEquals(0, pmt.accessLeafIndex(SecureHash.sha256("0")))
|
assertEquals(0, pmt.leafIndex(SecureHash.sha256("0")))
|
||||||
// Second leaf.
|
// Second leaf.
|
||||||
assertEquals(1, pmt.accessLeafIndex(SecureHash.sha256("1")))
|
assertEquals(1, pmt.leafIndex(SecureHash.sha256("1")))
|
||||||
// A random leaf.
|
// A random leaf.
|
||||||
assertEquals(5, pmt.accessLeafIndex(SecureHash.sha256("5")))
|
assertEquals(5, pmt.leafIndex(SecureHash.sha256("5")))
|
||||||
// The last leaf.
|
// The last leaf.
|
||||||
assertEquals(19, pmt.accessLeafIndex(SecureHash.sha256("19")))
|
assertEquals(19, pmt.leafIndex(SecureHash.sha256("19")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmt.accessLeafIndex(SecureHash.sha256("10")) }
|
assertFailsWith<MerkleTreeException> { pmt.leafIndex(SecureHash.sha256("10")) }
|
||||||
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
||||||
assertFailsWith<MerkleTreeException> { pmt.accessLeafIndex(SecureHash.sha256("30")) }
|
assertFailsWith<MerkleTreeException> { pmt.leafIndex(SecureHash.sha256("30")) }
|
||||||
|
|
||||||
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("0")))
|
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("0")))
|
||||||
assertEquals(0, pmtFirstElementOnly.accessLeafIndex(SecureHash.sha256("0")))
|
assertEquals(0, pmtFirstElementOnly.leafIndex(SecureHash.sha256("0")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.accessLeafIndex(SecureHash.sha256("10")) }
|
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(SecureHash.sha256("10")) }
|
||||||
|
|
||||||
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("19")))
|
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("19")))
|
||||||
assertEquals(19, pmtLastElementOnly.accessLeafIndex(SecureHash.sha256("19")))
|
assertEquals(19, pmtLastElementOnly.leafIndex(SecureHash.sha256("19")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.accessLeafIndex(SecureHash.sha256("10")) }
|
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(SecureHash.sha256("10")) }
|
||||||
|
|
||||||
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("5")))
|
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(SecureHash.sha256("5")))
|
||||||
assertEquals(5, pmtOneElement.accessLeafIndex(SecureHash.sha256("5")))
|
assertEquals(5, pmtOneElement.leafIndex(SecureHash.sha256("5")))
|
||||||
// The provided hash is not in the tree.
|
// The provided hash is not in the tree.
|
||||||
assertFailsWith<MerkleTreeException> { pmtOneElement.accessLeafIndex(SecureHash.sha256("10")) }
|
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(SecureHash.sha256("10")) }
|
||||||
|
|
||||||
val pmtAllIncluded = PartialMerkleTree.build(merkleTree, sampleLeaves)
|
val pmtAllIncluded = PartialMerkleTree.build(merkleTree, sampleLeaves)
|
||||||
for (i in 0..19) assertEquals(i, pmtAllIncluded.accessLeafIndex(SecureHash.sha256(i.toString())))
|
for (i in 0..19) assertEquals(i, pmtAllIncluded.leafIndex(SecureHash.sha256(i.toString())))
|
||||||
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
|
||||||
assertFailsWith<MerkleTreeException> { pmtAllIncluded.accessLeafIndex(SecureHash.sha256("30")) }
|
assertFailsWith<MerkleTreeException> { pmtAllIncluded.leafIndex(SecureHash.sha256("30")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
|
import net.corda.core.CordaInternal
|
||||||
import net.corda.core.KeepForDJVM
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -169,8 +170,9 @@ class PartialMerkleTree(val root: PartialTree) {
|
|||||||
* @return leaf-index of this component (starting from zero).
|
* @return leaf-index of this component (starting from zero).
|
||||||
* @throws MerkleTreeException if the provided hash is not in the tree.
|
* @throws MerkleTreeException if the provided hash is not in the tree.
|
||||||
*/
|
*/
|
||||||
|
@CordaInternal
|
||||||
@Throws(MerkleTreeException::class)
|
@Throws(MerkleTreeException::class)
|
||||||
internal fun leafIndex(leaf: SecureHash): Int {
|
fun leafIndex(leaf: SecureHash): Int {
|
||||||
// Special handling if the tree consists of one node only.
|
// Special handling if the tree consists of one node only.
|
||||||
if (root is PartialTree.IncludedLeaf && root.hash == leaf) return 0
|
if (root is PartialTree.IncludedLeaf && root.hash == leaf) return 0
|
||||||
val flagPath = mutableListOf<Boolean>()
|
val flagPath = mutableListOf<Boolean>()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
@file:KeepForDJVM
|
||||||
package net.corda.core.node.services
|
package net.corda.core.node.services
|
||||||
|
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
|
import net.corda.core.KeepForDJVM
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
|
||||||
|
@ -78,6 +78,7 @@ private constructor(
|
|||||||
checkNotaryWhitelisted()
|
checkNotaryWhitelisted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@KeepForDJVM
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
|
|
||||||
@ -126,6 +127,41 @@ private constructor(
|
|||||||
verifierFactory = ::BasicVerifier
|
verifierFactory = ::BasicVerifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This factory function will create an instance of [LedgerTransaction]
|
||||||
|
* that will be used inside the DJVM sandbox.
|
||||||
|
*/
|
||||||
|
@CordaInternal
|
||||||
|
fun createForSandbox(
|
||||||
|
inputs: List<StateAndRef<ContractState>>,
|
||||||
|
outputs: List<TransactionState<ContractState>>,
|
||||||
|
commands: List<CommandWithParties<CommandData>>,
|
||||||
|
attachments: List<Attachment>,
|
||||||
|
id: SecureHash,
|
||||||
|
notary: Party?,
|
||||||
|
timeWindow: TimeWindow?,
|
||||||
|
privacySalt: PrivacySalt,
|
||||||
|
networkParameters: NetworkParameters,
|
||||||
|
references: List<StateAndRef<ContractState>>): LedgerTransaction {
|
||||||
|
return LedgerTransaction(
|
||||||
|
inputs = inputs,
|
||||||
|
outputs = outputs,
|
||||||
|
commands = commands,
|
||||||
|
attachments = attachments,
|
||||||
|
id = id,
|
||||||
|
notary = notary,
|
||||||
|
timeWindow = timeWindow,
|
||||||
|
privacySalt = privacySalt,
|
||||||
|
networkParameters = networkParameters,
|
||||||
|
references = references,
|
||||||
|
componentGroups = null,
|
||||||
|
serializedInputs = null,
|
||||||
|
serializedReferences = null,
|
||||||
|
isAttachmentTrusted = { true },
|
||||||
|
verifierFactory = ::BasicVerifier
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
||||||
@ -179,8 +215,8 @@ private constructor(
|
|||||||
* Pass all of this [LedgerTransaction] object's serialized state to a [transformer] function.
|
* Pass all of this [LedgerTransaction] object's serialized state to a [transformer] function.
|
||||||
*/
|
*/
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
fun <T> transform(transformer: (List<ComponentGroup>?, List<SerializedStateAndRef>?, List<SerializedStateAndRef>?) -> T): T {
|
fun <T> transform(transformer: (List<ComponentGroup>, List<SerializedStateAndRef>, List<SerializedStateAndRef>) -> T): T {
|
||||||
return transformer(componentGroups, serializedInputs, serializedReferences)
|
return transformer(componentGroups ?: emptyList(), serializedInputs ?: emptyList(), serializedReferences ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package net.corda.core.internal
|
package net.corda.core.internal
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.PartialMerkleTree
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
@ -37,5 +36,3 @@ fun createLedgerTransaction(
|
|||||||
|
|
||||||
fun createContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) = TransactionVerificationException.ContractCreationError(txId, contractClass, cause)
|
fun createContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) = TransactionVerificationException.ContractCreationError(txId, contractClass, cause)
|
||||||
fun createContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) = TransactionVerificationException.ContractRejection(txId, contract, cause)
|
fun createContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) = TransactionVerificationException.ContractRejection(txId, contract, cause)
|
||||||
|
|
||||||
fun PartialMerkleTree.accessLeafIndex(id: SecureHash) = this.leafIndex(id)
|
|
@ -181,12 +181,18 @@ dependencies {
|
|||||||
// Sandbox for deterministic contract verification
|
// Sandbox for deterministic contract verification
|
||||||
compile "net.corda:corda-djvm:$djvm_version"
|
compile "net.corda:corda-djvm:$djvm_version"
|
||||||
compile "net.corda:corda-djvm-serialization:$djvm_version"
|
compile "net.corda:corda-djvm-serialization:$djvm_version"
|
||||||
|
compile(project(':node:djvm')) {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
jdkRt "net.corda:deterministic-rt:latest.integration"
|
jdkRt "net.corda:deterministic-rt:latest.integration"
|
||||||
deterministic project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
|
deterministic project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
|
||||||
deterministic project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
|
deterministic project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
|
||||||
deterministic("net.corda:corda-djvm-deserializers:$djvm_version") {
|
deterministic("net.corda:corda-djvm-deserializers:$djvm_version") {
|
||||||
transitive = false
|
transitive = false
|
||||||
}
|
}
|
||||||
|
deterministic(project(':node:djvm')) {
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
deterministic "org.slf4j:slf4j-nop:$slf4j_version"
|
deterministic "org.slf4j:slf4j-nop:$slf4j_version"
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||||
|
20
node/djvm/build.gradle
Normal file
20
node/djvm/build.gradle
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
apply from: '../../deterministic.gradle'
|
||||||
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
|
description 'Internal Corda Node modules for deterministic contract verification.'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||||
|
api project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
|
||||||
|
api project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
archiveBaseName = 'corda-node-djvm'
|
||||||
|
}
|
||||||
|
|
||||||
|
publish {
|
||||||
|
name jar.archiveBaseName.get()
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package net.corda.node.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.util.Collections.unmodifiableList
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class AttachmentBuilder : Function<Array<Any>?, List<Attachment>?> {
|
||||||
|
private val attachments = mutableListOf<Attachment>()
|
||||||
|
|
||||||
|
private fun <T> unmodifiable(list: List<T>): List<T> {
|
||||||
|
return if(list.isEmpty()) {
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
unmodifiableList(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun apply(inputs: Array<Any>?): List<Attachment>? {
|
||||||
|
return if (inputs == null) {
|
||||||
|
unmodifiable(attachments)
|
||||||
|
} else {
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
attachments.add(SandboxAttachment(
|
||||||
|
signerKeys = inputs[0] as List<PublicKey>,
|
||||||
|
size = inputs[1] as Int,
|
||||||
|
id = inputs[2] as SecureHash,
|
||||||
|
attachment = inputs[3],
|
||||||
|
streamer = inputs[4] as Function<in Any, out InputStream>
|
||||||
|
))
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents an [Attachment] from within the sandbox.
|
||||||
|
*/
|
||||||
|
class SandboxAttachment(
|
||||||
|
override val signerKeys: List<PublicKey>,
|
||||||
|
override val size: Int,
|
||||||
|
override val id: SecureHash,
|
||||||
|
private val attachment: Any,
|
||||||
|
private val streamer: Function<Any, out InputStream>
|
||||||
|
) : Attachment {
|
||||||
|
@Suppress("OverridingDeprecatedMember")
|
||||||
|
override val signers: List<Party> = emptyList()
|
||||||
|
|
||||||
|
override fun open(): InputStream {
|
||||||
|
return streamer.apply(attachment)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package net.corda.node.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.CommandWithParties
|
||||||
|
import net.corda.core.internal.lazyMapped
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class CommandBuilder : Function<Array<Any?>, List<CommandWithParties<CommandData>>> {
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
override fun apply(inputs: Array<Any?>): List<CommandWithParties<CommandData>> {
|
||||||
|
val signers = inputs[0] as? List<List<PublicKey>> ?: emptyList()
|
||||||
|
val commandsData = inputs[1] as? List<CommandData> ?: emptyList()
|
||||||
|
val partialMerkleLeafIndices = inputs[2] as? IntArray
|
||||||
|
|
||||||
|
return if (partialMerkleLeafIndices != null) {
|
||||||
|
check(commandsData.size <= signers.size) {
|
||||||
|
"Invalid Transaction. Fewer Signers (${signers.size}) than CommandData (${commandsData.size}) objects"
|
||||||
|
}
|
||||||
|
if (partialMerkleLeafIndices.isNotEmpty()) {
|
||||||
|
check(partialMerkleLeafIndices.max()!! < signers.size) {
|
||||||
|
"Invalid Transaction. A command with no corresponding signer detected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandsData.lazyMapped { commandData, index ->
|
||||||
|
CommandWithParties(signers[partialMerkleLeafIndices[index]], emptyList(), commandData)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check(commandsData.size == signers.size) {
|
||||||
|
"Invalid Transaction. Sizes of CommandData (${commandsData.size}) and Signers (${signers.size}) do not match"
|
||||||
|
}
|
||||||
|
commandsData.lazyMapped { commandData, index ->
|
||||||
|
CommandWithParties(signers[index], emptyList(), commandData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.corda.node.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.ComponentGroupEnum
|
||||||
|
import net.corda.core.internal.TransactionDeserialisationException
|
||||||
|
import net.corda.core.internal.lazyMapped
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class ComponentBuilder : Function<Array<Any?>, List<*>> {
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
override fun apply(inputs: Array<Any?>): List<*> {
|
||||||
|
val deserializer = inputs[0] as Function<in Any?, out Any?>
|
||||||
|
val groupType = inputs[1] as ComponentGroupEnum
|
||||||
|
val components = (inputs[2] as Array<ByteArray>).map { OpaqueBytes(it) }
|
||||||
|
|
||||||
|
return components.lazyMapped { component, index ->
|
||||||
|
try {
|
||||||
|
deserializer.apply(component.bytes)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw TransactionDeserialisationException(groupType, index, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
node/djvm/src/main/kotlin/net/corda/node/djvm/LtxFactory.kt
Normal file
43
node/djvm/src/main/kotlin/net/corda/node/djvm/LtxFactory.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
@file:JvmName("LtxConstants")
|
||||||
|
package net.corda.node.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
private const val TX_INPUTS = 0
|
||||||
|
private const val TX_OUTPUTS = 1
|
||||||
|
private const val TX_COMMANDS = 2
|
||||||
|
private const val TX_ATTACHMENTS = 3
|
||||||
|
private const val TX_ID = 4
|
||||||
|
private const val TX_NOTARY = 5
|
||||||
|
private const val TX_TIME_WINDOW = 6
|
||||||
|
private const val TX_PRIVACY_SALT = 7
|
||||||
|
private const val TX_NETWORK_PARAMETERS = 8
|
||||||
|
private const val TX_REFERENCES = 9
|
||||||
|
|
||||||
|
class LtxFactory : Function<Array<Any?>, LedgerTransaction> {
|
||||||
|
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
override fun apply(txArgs: Array<Any?>): LedgerTransaction {
|
||||||
|
return LedgerTransaction.createForSandbox(
|
||||||
|
inputs = (txArgs[TX_INPUTS] as Array<Array<Any?>>).map { it.toStateAndRef() },
|
||||||
|
outputs = (txArgs[TX_OUTPUTS] as? List<TransactionState<ContractState>>) ?: emptyList(),
|
||||||
|
commands = (txArgs[TX_COMMANDS] as? List<CommandWithParties<CommandData>>) ?: emptyList(),
|
||||||
|
attachments = (txArgs[TX_ATTACHMENTS] as? List<Attachment>) ?: emptyList(),
|
||||||
|
id = txArgs[TX_ID] as SecureHash,
|
||||||
|
notary = txArgs[TX_NOTARY] as? Party,
|
||||||
|
timeWindow = txArgs[TX_TIME_WINDOW] as? TimeWindow,
|
||||||
|
privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt,
|
||||||
|
networkParameters = txArgs[TX_NETWORK_PARAMETERS] as NetworkParameters,
|
||||||
|
references = (txArgs[TX_REFERENCES] as Array<Array<Any?>>).map { it.toStateAndRef() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Array<*>.toStateAndRef(): StateAndRef<ContractState> {
|
||||||
|
return StateAndRef(this[0] as TransactionState<*>, this[1] as StateRef)
|
||||||
|
}
|
||||||
|
}
|
@ -33,14 +33,11 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.djvm.analysis.AnalysisConfiguration
|
|
||||||
import net.corda.djvm.analysis.Whitelist
|
|
||||||
import net.corda.djvm.source.*
|
import net.corda.djvm.source.*
|
||||||
import net.corda.node.CordaClock
|
import net.corda.node.CordaClock
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.classloading.requireAnnotation
|
import net.corda.node.internal.classloading.requireAnnotation
|
||||||
import net.corda.node.internal.cordapp.*
|
import net.corda.node.internal.cordapp.*
|
||||||
import net.corda.node.internal.djvm.DeterministicVerifier
|
|
||||||
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
|
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
|
||||||
import net.corda.node.internal.rpc.proxies.ThreadContextAdjustingRpcOpsProxy
|
import net.corda.node.internal.rpc.proxies.ThreadContextAdjustingRpcOpsProxy
|
||||||
import net.corda.node.services.ContractUpgradeHandler
|
import net.corda.node.services.ContractUpgradeHandler
|
||||||
@ -68,6 +65,7 @@ import net.corda.node.services.persistence.*
|
|||||||
import net.corda.node.services.rpc.CheckpointDumper
|
import net.corda.node.services.rpc.CheckpointDumper
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.services.statemachine.*
|
import net.corda.node.services.statemachine.*
|
||||||
|
import net.corda.node.services.transactions.DeterministicVerifierFactoryService
|
||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||||
@ -98,8 +96,6 @@ import rx.Observable
|
|||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.net.URL
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
@ -221,6 +217,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
).closeOnStop()
|
).closeOnStop()
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
val transactionVerifierService = InMemoryTransactionVerifierService(transactionVerifierWorkerCount).tokenize()
|
val transactionVerifierService = InMemoryTransactionVerifierService(transactionVerifierWorkerCount).tokenize()
|
||||||
|
val deterministicVerifierFactoryService = DeterministicVerifierFactoryService(djvmBootstrapSource, djvmCordaSource).tokenize()
|
||||||
val contractUpgradeService = ContractUpgradeServiceImpl(cacheFactory).tokenize()
|
val contractUpgradeService = ContractUpgradeServiceImpl(cacheFactory).tokenize()
|
||||||
val auditService = DummyAuditService().tokenize()
|
val auditService = DummyAuditService().tokenize()
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
@ -1081,28 +1078,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val ledgerTransaction = servicesForResolution.specialise(ltx)
|
val ledgerTransaction = servicesForResolution.specialise(ltx)
|
||||||
|
|
||||||
// Do nothing unless we have Corda's deterministic libraries.
|
// Do nothing unless we have Corda's deterministic libraries.
|
||||||
val cordaSource = djvmCordaSource ?: return ledgerTransaction
|
djvmCordaSource ?: return ledgerTransaction
|
||||||
|
|
||||||
// Specialise the LedgerTransaction here so that
|
// Specialise the LedgerTransaction here so that
|
||||||
// contracts are verified inside the DJVM!
|
// contracts are verified inside the DJVM!
|
||||||
return ledgerTransaction.specialise { tx, cl ->
|
return ledgerTransaction.specialise(deterministicVerifierFactoryService::specialise)
|
||||||
(cl as? URLClassLoader)?.run { DeterministicVerifier(tx, cl, createSandbox(cordaSource, cl.urLs)) } ?: BasicVerifier(tx, cl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createSandbox(cordaSource: UserSource, userSource: Array<URL>): AnalysisConfiguration {
|
|
||||||
return AnalysisConfiguration.createRoot(
|
|
||||||
userSource = cordaSource,
|
|
||||||
whitelist = Whitelist.MINIMAL,
|
|
||||||
bootstrapSource = djvmBootstrapSource
|
|
||||||
).createChild(
|
|
||||||
userSource = UserPathSource(userSource),
|
|
||||||
newMinimumSeverityLevel = null,
|
|
||||||
visibleAnnotations = setOf(
|
|
||||||
CordaSerializable::class.java,
|
|
||||||
ConstructorForDeserialization::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.Attachment
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.djvm.rewiring.SandboxClassLoader
|
||||||
|
import net.corda.node.djvm.AttachmentBuilder
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class AttachmentFactory(
|
||||||
|
private val classLoader: SandboxClassLoader,
|
||||||
|
private val taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
|
||||||
|
private val sandboxBasicInput: Function<in Any?, out Any?>,
|
||||||
|
private val serializer: Serializer
|
||||||
|
) {
|
||||||
|
private val sandboxOpenAttachment: Function<in Attachment, out Any?> = classLoader.createForImport(
|
||||||
|
Function(Attachment::open).andThen(sandboxBasicInput)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toSandbox(attachments: List<Attachment>): Any? {
|
||||||
|
val builder = classLoader.createTaskFor(taskFactory, AttachmentBuilder::class.java)
|
||||||
|
for (attachment in attachments) {
|
||||||
|
builder.apply(arrayOf(
|
||||||
|
serializer.deserialize(attachment.signerKeys.serialize()),
|
||||||
|
sandboxBasicInput.apply(attachment.size),
|
||||||
|
serializer.deserialize(attachment.id.serialize()),
|
||||||
|
attachment,
|
||||||
|
sandboxOpenAttachment
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return builder.apply(null)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
|
import net.corda.djvm.rewiring.SandboxClassLoader
|
||||||
|
import net.corda.node.djvm.CommandBuilder
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class CommandFactory(
|
||||||
|
private val classLoader: SandboxClassLoader,
|
||||||
|
private val taskFactory: Function<in Any, out Function<in Any?, out Any?>>
|
||||||
|
) {
|
||||||
|
fun toSandbox(signers: Any?, commands: Any?, partialMerkleLeafIndices: IntArray?): Any? {
|
||||||
|
val builder = classLoader.createTaskFor(taskFactory, CommandBuilder::class.java)
|
||||||
|
return builder.apply(arrayOf(
|
||||||
|
signers,
|
||||||
|
commands,
|
||||||
|
partialMerkleLeafIndices
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
@file:JvmName("ComponentUtils")
|
||||||
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.ComponentGroupEnum
|
||||||
|
import net.corda.core.crypto.componentHash
|
||||||
|
import net.corda.core.transactions.ComponentGroup
|
||||||
|
import net.corda.core.transactions.FilteredComponentGroup
|
||||||
|
import net.corda.djvm.rewiring.SandboxClassLoader
|
||||||
|
import net.corda.node.djvm.ComponentBuilder
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class ComponentFactory(
|
||||||
|
private val classLoader: SandboxClassLoader,
|
||||||
|
private val taskFactory: Function<in Any, out Function<in Any?, out Any?>>,
|
||||||
|
private val sandboxBasicInput: Function<in Any?, out Any?>,
|
||||||
|
private val serializer: Serializer,
|
||||||
|
private val componentGroups: List<ComponentGroup>
|
||||||
|
) {
|
||||||
|
fun toSandbox(
|
||||||
|
groupType: ComponentGroupEnum,
|
||||||
|
clazz: Class<*>
|
||||||
|
): Any? {
|
||||||
|
val components = (componentGroups.firstOrNull(groupType::isSameType) ?: return null).components
|
||||||
|
val componentBytes = Array(components.size) { idx -> components[idx].bytes }
|
||||||
|
return classLoader.createTaskFor(taskFactory, ComponentBuilder::class.java).apply(arrayOf(
|
||||||
|
classLoader.createForImport(serializer.deserializerFor(clazz)),
|
||||||
|
sandboxBasicInput.apply(groupType),
|
||||||
|
componentBytes
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateLeafIndicesFor(groupType: ComponentGroupEnum): IntArray? {
|
||||||
|
val componentGroup = componentGroups.firstOrNull(groupType::isSameType) as? FilteredComponentGroup ?: return null
|
||||||
|
val componentHashes = componentGroup.components.mapIndexed { index, component ->
|
||||||
|
componentHash(componentGroup.nonces[index], component)
|
||||||
|
}
|
||||||
|
return componentHashes.map { componentGroup.partialMerkleTree.leafIndex(it) }.toIntArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ComponentGroupEnum.isSameType(group: ComponentGroup): Boolean {
|
||||||
|
return group.groupIndex == ordinal
|
||||||
|
}
|
@ -1,15 +1,19 @@
|
|||||||
package net.corda.node.internal.djvm
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.ComponentGroupEnum.*
|
||||||
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.contracts.TransactionVerificationException
|
import net.corda.core.contracts.TransactionVerificationException
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.ContractVerifier
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.internal.Verifier
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.djvm.SandboxConfiguration
|
import net.corda.djvm.SandboxConfiguration
|
||||||
import net.corda.djvm.analysis.AnalysisConfiguration
|
import net.corda.djvm.analysis.AnalysisConfiguration
|
||||||
import net.corda.djvm.execution.*
|
import net.corda.djvm.execution.*
|
||||||
import net.corda.djvm.messages.Message
|
import net.corda.djvm.messages.Message
|
||||||
import net.corda.djvm.source.ClassSource
|
import net.corda.djvm.source.ClassSource
|
||||||
|
import net.corda.node.djvm.LtxFactory
|
||||||
|
|
||||||
class DeterministicVerifier(
|
class DeterministicVerifier(
|
||||||
ltx: LedgerTransaction,
|
ltx: LedgerTransaction,
|
||||||
@ -23,24 +27,67 @@ class DeterministicVerifier(
|
|||||||
profile = ExecutionProfile.DEFAULT,
|
profile = ExecutionProfile.DEFAULT,
|
||||||
enableTracing = false
|
enableTracing = false
|
||||||
)
|
)
|
||||||
val verifierClass = ClassSource.fromClassName(ContractVerifier::class.java.name)
|
|
||||||
val result = IsolatedTask(verifierClass.qualifiedClassName, configuration).run {
|
|
||||||
val executor = Executor(classLoader)
|
|
||||||
|
|
||||||
|
val result = IsolatedTask(ltx.id.toString(), configuration).run {
|
||||||
|
val taskFactory = classLoader.createRawTaskFactory()
|
||||||
|
val sandboxBasicInput = classLoader.createBasicInput()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialise the [LedgerTransaction] again into something
|
||||||
|
* that we can execute inside the DJVM's sandbox.
|
||||||
|
*/
|
||||||
val sandboxTx = ltx.transform { componentGroups, serializedInputs, serializedReferences ->
|
val sandboxTx = ltx.transform { componentGroups, serializedInputs, serializedReferences ->
|
||||||
|
val serializer = Serializer(classLoader)
|
||||||
|
val componentFactory = ComponentFactory(
|
||||||
|
classLoader,
|
||||||
|
taskFactory,
|
||||||
|
sandboxBasicInput,
|
||||||
|
serializer,
|
||||||
|
componentGroups
|
||||||
|
)
|
||||||
|
val attachmentFactory = AttachmentFactory(
|
||||||
|
classLoader,
|
||||||
|
taskFactory,
|
||||||
|
sandboxBasicInput,
|
||||||
|
serializer
|
||||||
|
)
|
||||||
|
|
||||||
|
val idData = ltx.id.serialize()
|
||||||
|
val notaryData = ltx.notary?.serialize()
|
||||||
|
val timeWindowData = ltx.timeWindow?.serialize()
|
||||||
|
val privacySaltData = ltx.privacySalt.serialize()
|
||||||
|
val networkingParametersData = ltx.networkParameters?.serialize()
|
||||||
|
|
||||||
|
val createSandboxTx = classLoader.createTaskFor(taskFactory, LtxFactory::class.java)
|
||||||
|
createSandboxTx.apply(arrayOf(
|
||||||
|
serializer.deserialize(serializedInputs),
|
||||||
|
componentFactory.toSandbox(OUTPUTS_GROUP, TransactionState::class.java),
|
||||||
|
CommandFactory(classLoader, taskFactory).toSandbox(
|
||||||
|
componentFactory.toSandbox(SIGNERS_GROUP, List::class.java),
|
||||||
|
componentFactory.toSandbox(COMMANDS_GROUP, CommandData::class.java),
|
||||||
|
componentFactory.calculateLeafIndicesFor(COMMANDS_GROUP)
|
||||||
|
),
|
||||||
|
attachmentFactory.toSandbox(ltx.attachments),
|
||||||
|
serializer.deserialize(idData),
|
||||||
|
serializer.deserialize(notaryData),
|
||||||
|
serializer.deserialize(timeWindowData),
|
||||||
|
serializer.deserialize(privacySaltData),
|
||||||
|
serializer.deserialize(networkingParametersData),
|
||||||
|
serializer.deserialize(serializedReferences)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
val verifier = classLoader.loadClassForSandbox(verifierClass).newInstance()
|
val verifier = classLoader.createTaskFor(taskFactory, ContractVerifier::class.java)
|
||||||
|
|
||||||
// Now execute the contract verifier task within the sandbox...
|
// Now execute the contract verifier task within the sandbox...
|
||||||
executor.execute(verifier, sandboxTx)
|
verifier.apply(sandboxTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.exception?.run {
|
result.exception?.run {
|
||||||
val sandboxEx = SandboxException(
|
val sandboxEx = SandboxException(
|
||||||
Message.getMessageFromException(this),
|
Message.getMessageFromException(this),
|
||||||
result.identifier,
|
result.identifier,
|
||||||
verifierClass,
|
ClassSource.fromClassName(ContractVerifier::class.java.name),
|
||||||
ExecutionSummary(result.costs),
|
ExecutionSummary(result.costs),
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
@ -51,7 +98,7 @@ class DeterministicVerifier(
|
|||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun close() {
|
override fun close() {
|
||||||
analysisConfiguration.closeAll()
|
// analysisConfiguration.closeAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package net.corda.node.internal.djvm
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor
|
|
||||||
import java.lang.reflect.InvocationTargetException
|
|
||||||
import java.lang.reflect.Method
|
|
||||||
|
|
||||||
class Executor(classLoader: ClassLoader) {
|
|
||||||
private val constructor: Constructor<out Any>
|
|
||||||
private val executeMethod: Method
|
|
||||||
|
|
||||||
init {
|
|
||||||
val taskClass = classLoader.loadClass("sandbox.RawTask")
|
|
||||||
constructor = taskClass.getDeclaredConstructor(classLoader.loadClass("sandbox.java.util.function.Function"))
|
|
||||||
executeMethod = taskClass.getMethod("apply", Any::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun execute(task: Any, input: Any?): Any? {
|
|
||||||
return try {
|
|
||||||
executeMethod.invoke(constructor.newInstance(task), input)
|
|
||||||
} catch (ex: InvocationTargetException) {
|
|
||||||
throw ex.targetException
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,59 @@
|
|||||||
|
package net.corda.node.internal.djvm
|
||||||
|
|
||||||
|
import net.corda.core.internal.SerializedStateAndRef
|
||||||
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.core.serialization.SerializationFactory
|
||||||
|
import net.corda.core.serialization.SerializedBytes
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.utilities.ByteSequence
|
||||||
|
import net.corda.djvm.rewiring.SandboxClassLoader
|
||||||
|
import net.corda.djvm.serialization.createSandboxSerializationEnv
|
||||||
|
import net.corda.djvm.serialization.deserializeTo
|
||||||
|
import net.corda.node.djvm.ComponentBuilder
|
||||||
|
import java.util.function.Function
|
||||||
|
|
||||||
|
class Serializer(private val classLoader: SandboxClassLoader) {
|
||||||
|
private val factory: SerializationFactory
|
||||||
|
private val context: SerializationContext
|
||||||
|
|
||||||
|
init {
|
||||||
|
val env = createSandboxSerializationEnv(classLoader)
|
||||||
|
factory = env.serializationFactory
|
||||||
|
context = env.p2pContext
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a list of [SerializedStateAndRef] objects into arrays
|
||||||
|
* of deserialized sandbox objects. We will pass this array into
|
||||||
|
* [net.corda.node.djvm.LtxFactory] to be transformed finally to
|
||||||
|
* a list of [net.corda.core.contracts.StateAndRef] objects,
|
||||||
|
*/
|
||||||
|
fun deserialize(stateRefs: List<SerializedStateAndRef>): Array<Array<Any?>> {
|
||||||
|
return stateRefs.map {
|
||||||
|
arrayOf(deserialize(it.serializedState), deserialize(it.ref.serialize()))
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [Function] that deserializes a [ByteArray] into an instance
|
||||||
|
* of the given sandbox class. We import this [Function] into the sandbox
|
||||||
|
* so that [ComponentBuilder] can deserialize objects lazily.
|
||||||
|
*/
|
||||||
|
fun deserializerFor(clazz: Class<*>): Function<ByteArray?, out Any?> {
|
||||||
|
val sandboxClass = classLoader.toSandboxClass(clazz)
|
||||||
|
return Function { bytes ->
|
||||||
|
bytes?.run {
|
||||||
|
ByteSequence.of(this).deserializeTo(sandboxClass, classLoader, factory, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deserializeTo(clazz: Class<*>, bytes: ByteSequence): Any {
|
||||||
|
val sandboxClass = classLoader.toSandboxClass(clazz)
|
||||||
|
return bytes.deserializeTo(sandboxClass, classLoader, factory, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>?): Any? {
|
||||||
|
return deserializeTo(T::class.java, bytes ?: return null)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package net.corda.node.services.transactions
|
||||||
|
|
||||||
|
import net.corda.core.internal.BasicVerifier
|
||||||
|
import net.corda.core.internal.Verifier
|
||||||
|
import net.corda.core.serialization.ConstructorForDeserialization
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||||
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import net.corda.djvm.analysis.AnalysisConfiguration
|
||||||
|
import net.corda.djvm.analysis.Whitelist
|
||||||
|
import net.corda.djvm.source.ApiSource
|
||||||
|
import net.corda.djvm.source.UserPathSource
|
||||||
|
import net.corda.djvm.source.UserSource
|
||||||
|
import net.corda.node.internal.djvm.DeterministicVerifier
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
|
class DeterministicVerifierFactoryService(
|
||||||
|
private val bootstrapSource: ApiSource,
|
||||||
|
private val cordaSource: UserSource?
|
||||||
|
) : SingletonSerializeAsToken(), AutoCloseable {
|
||||||
|
|
||||||
|
fun specialise(ltx: LedgerTransaction, classLoader: ClassLoader): Verifier {
|
||||||
|
return (classLoader as? URLClassLoader)?.run {
|
||||||
|
DeterministicVerifier(ltx, classLoader, createSandbox(classLoader.urLs))
|
||||||
|
} ?: BasicVerifier(ltx, classLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSandbox(userSource: Array<URL>): AnalysisConfiguration {
|
||||||
|
return AnalysisConfiguration.createRoot(
|
||||||
|
userSource = cordaSource!!,
|
||||||
|
whitelist = Whitelist.MINIMAL,
|
||||||
|
visibleAnnotations = setOf(
|
||||||
|
CordaSerializable::class.java,
|
||||||
|
ConstructorForDeserialization::class.java,
|
||||||
|
DeprecatedConstructorForDeserialization::class.java
|
||||||
|
),
|
||||||
|
bootstrapSource = bootstrapSource
|
||||||
|
).createChild(
|
||||||
|
userSource = UserPathSource(userSource),
|
||||||
|
newMinimumSeverityLevel = null,
|
||||||
|
visibleAnnotations = emptySet()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
bootstrapSource.use {
|
||||||
|
cordaSource?.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ include 'docs'
|
|||||||
include 'node-api'
|
include 'node-api'
|
||||||
include 'node'
|
include 'node'
|
||||||
include 'node:capsule'
|
include 'node:capsule'
|
||||||
|
include 'node:djvm'
|
||||||
include 'client:jackson'
|
include 'client:jackson'
|
||||||
include 'client:jfx'
|
include 'client:jfx'
|
||||||
include 'client:mock'
|
include 'client:mock'
|
||||||
|
Loading…
Reference in New Issue
Block a user