mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
Add the Corda deterministic libraries to the Node, and split the DJVM sandbox across two SandboxClassLoader instances.
This commit is contained in:
parent
d4da84cc05
commit
378635475a
@ -12,6 +12,7 @@ import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import java.util.*
|
||||
import java.util.Collections.unmodifiableList
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
@ -80,6 +81,16 @@ private constructor(
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
|
||||
private fun <T> protect(list: List<T>?): List<T>? {
|
||||
return list?.run {
|
||||
if (isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
unmodifiableList(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CordaInternal
|
||||
internal fun create(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
@ -108,9 +119,9 @@ private constructor(
|
||||
privacySalt = privacySalt,
|
||||
networkParameters = networkParameters,
|
||||
references = references,
|
||||
componentGroups = componentGroups,
|
||||
serializedInputs = serializedInputs,
|
||||
serializedReferences = serializedReferences,
|
||||
componentGroups = protect(componentGroups),
|
||||
serializedInputs = protect(serializedInputs),
|
||||
serializedReferences = protect(serializedReferences),
|
||||
isAttachmentTrusted = isAttachmentTrusted,
|
||||
verifierFactory = ::Verifier
|
||||
)
|
||||
@ -164,6 +175,14 @@ private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass all of this [LedgerTransaction] object's serialized state to a [transformer] function.
|
||||
*/
|
||||
@CordaInternal
|
||||
fun <T> transform(transformer: (List<ComponentGroup>?, List<SerializedStateAndRef>?, List<SerializedStateAndRef>?) -> T): T {
|
||||
return transformer(componentGroups, serializedInputs, serializedReferences)
|
||||
}
|
||||
|
||||
/**
|
||||
* We need a way to customise transaction verification inside the
|
||||
* Node without changing either the wire format or any public APIs.
|
||||
|
@ -42,6 +42,7 @@ configurations {
|
||||
jdkRt.resolutionStrategy {
|
||||
cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
deterministic
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -163,6 +164,9 @@ dependencies {
|
||||
// Sandbox for deterministic contract verification
|
||||
compile "net.corda:corda-djvm:$djvm_version"
|
||||
jdkRt "net.corda:deterministic-rt:latest.integration"
|
||||
deterministic project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
|
||||
deterministic project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
|
||||
deterministic "org.slf4j:slf4j-nop:$slf4j_version"
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@ -299,6 +303,10 @@ quasar {
|
||||
|
||||
jar {
|
||||
baseName 'corda-node'
|
||||
manifest {
|
||||
attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name)
|
||||
attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' '))
|
||||
}
|
||||
}
|
||||
|
||||
publish {
|
||||
|
@ -36,7 +36,11 @@ capsule {
|
||||
version capsule_version
|
||||
}
|
||||
|
||||
task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
|
||||
task buildCordaJAR(type: FatCapsule, dependsOn: [
|
||||
project(':node').tasks.jar,
|
||||
project(':core-deterministic').tasks.assemble,
|
||||
project(':serialization-deterministic').tasks.assemble
|
||||
]) {
|
||||
applicationClass 'net.corda.node.Corda'
|
||||
archiveBaseName = 'corda'
|
||||
archiveVersion = corda_release_version
|
||||
@ -52,9 +56,18 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
|
||||
from configurations.capsuleRuntime.files.collect { zipTree(it) }
|
||||
with jar
|
||||
|
||||
def deterministicResolved = project(':node').configurations['deterministic'].resolvedConfiguration
|
||||
def deterministicLibs = deterministicResolved.firstLevelModuleDependencies.moduleArtifacts.flatten { it.file }.toSet()
|
||||
def deterministicCordaDependencies = deterministicResolved.files - deterministicLibs
|
||||
|
||||
manifest {
|
||||
// These are the dependencies that the deterministic Corda libraries share with Corda.
|
||||
attributes('Corda-DJVM-Dependencies': deterministicCordaDependencies.collect { it.name }.join(' '))
|
||||
}
|
||||
|
||||
into('djvm') {
|
||||
from project(':node').configurations['jdkRt'].singleFile
|
||||
rename 'deterministic-rt(.*)', 'deterministic-rt.jar'
|
||||
from deterministicLibs
|
||||
fileMode = 0444
|
||||
}
|
||||
|
||||
|
@ -7,20 +7,20 @@ import sun.misc.Signal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
|
||||
public class CordaCaplet extends Capsule {
|
||||
private static final String DETERMINISTIC_RT = "deterministic-rt.jar";
|
||||
private static final String DJVM_DIR ="djvm";
|
||||
private static final String DETERMINISTIC_RT_RESOURCE = "/" + DJVM_DIR + "/" + DETERMINISTIC_RT;
|
||||
|
||||
private Config nodeConfig = null;
|
||||
private String baseDir = null;
|
||||
@ -93,27 +93,56 @@ public class CordaCaplet extends Capsule {
|
||||
if (!djvmDir.toFile().mkdir() && !Files.isDirectory(djvmDir)) {
|
||||
log(LOG_VERBOSE, "DJVM directory could not be created");
|
||||
} else {
|
||||
Path deterministicRt = djvmDir.resolve(DETERMINISTIC_RT);
|
||||
Path sourceRt = appDir().resolve(DJVM_DIR).resolve(DETERMINISTIC_RT);
|
||||
if (Files.isRegularFile(sourceRt)) {
|
||||
try {
|
||||
// Forcibly reinstall the deterministic APIs.
|
||||
Files.deleteIfExists(deterministicRt);
|
||||
Files.createSymbolicLink(deterministicRt, sourceRt);
|
||||
} catch (UnsupportedOperationException | IOException e) {
|
||||
copyFile(sourceRt, deterministicRt);
|
||||
}
|
||||
} else {
|
||||
URL rtURL = getClass().getResource(DETERMINISTIC_RT_RESOURCE);
|
||||
if (rtURL == null) {
|
||||
log(LOG_VERBOSE, DETERMINISTIC_RT_RESOURCE + " missing from Corda capsule");
|
||||
} else {
|
||||
copyResource(rtURL, deterministicRt);
|
||||
try {
|
||||
Path sourceDir = appDir().resolve(DJVM_DIR);
|
||||
if (Files.isDirectory(sourceDir)) {
|
||||
installCordaDependenciesForDJVM(sourceDir, djvmDir);
|
||||
installTransitiveDependenciesForDJVM(appDir(), djvmDir);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log(LOG_VERBOSE, "Failed to populate directory " + djvmDir.toAbsolutePath());
|
||||
log(LOG_VERBOSE, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void installCordaDependenciesForDJVM(Path sourceDir, Path targetDir) throws IOException {
|
||||
try (DirectoryStream<Path> directory = Files.newDirectoryStream(sourceDir, file -> Files.isRegularFile(file))) {
|
||||
for (Path sourceFile : directory) {
|
||||
Path targetFile = targetDir.resolve(sourceFile.getFileName());
|
||||
installFile(sourceFile, targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void installTransitiveDependenciesForDJVM(Path sourceDir, Path targetDir) throws IOException {
|
||||
Manifest manifest = getManifest();
|
||||
String[] transitives = manifest.getMainAttributes().getValue("Corda-DJVM-Dependencies").split("\\s++", 0);
|
||||
for (String transitive : transitives) {
|
||||
Path source = sourceDir.resolve(transitive);
|
||||
if (Files.isRegularFile(source)) {
|
||||
installFile(source, targetDir.resolve(transitive));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Manifest getManifest() throws IOException {
|
||||
URL capsule = getClass().getProtectionDomain().getCodeSource().getLocation();
|
||||
try (JarInputStream jar = new JarInputStream(capsule.openStream())) {
|
||||
return jar.getManifest();
|
||||
}
|
||||
}
|
||||
|
||||
private void installFile(Path source, Path target) {
|
||||
try {
|
||||
// Forcibly reinstall this dependency.
|
||||
Files.deleteIfExists(target);
|
||||
Files.createSymbolicLink(target, source);
|
||||
} catch (UnsupportedOperationException | IOException e) {
|
||||
copyFile(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(Path source, Path target) {
|
||||
try {
|
||||
Files.copy(source, target, REPLACE_EXISTING);
|
||||
@ -124,16 +153,6 @@ public class CordaCaplet extends Capsule {
|
||||
}
|
||||
}
|
||||
|
||||
private void copyResource(URL source, Path target) {
|
||||
try (InputStream input = source.openStream()) {
|
||||
Files.copy(input, target, REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
target.toFile().delete();
|
||||
log(LOG_VERBOSE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProcessBuilder prelaunch(List<String> jvmArgs, List<String> args) {
|
||||
checkJavaVersion();
|
||||
|
@ -34,9 +34,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.djvm.source.ApiSource
|
||||
import net.corda.djvm.source.BootstrapClassLoader
|
||||
import net.corda.djvm.source.EmptyApi
|
||||
import net.corda.djvm.source.*
|
||||
import net.corda.node.CordaClock
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
@ -129,7 +127,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
protected val flowManager: FlowManager,
|
||||
val serverThread: AffinityExecutor.ServiceAffinityExecutor,
|
||||
val busyNodeLatch: ReusableLatch = ReusableLatch(),
|
||||
bootstrapSource: ApiSource = EmptyApi) : SingletonSerializeAsToken() {
|
||||
bootstrapSource: ApiSource = EmptyApi,
|
||||
djvmCordaSource: UserSource? = null) : SingletonSerializeAsToken() {
|
||||
|
||||
protected abstract val log: Logger
|
||||
@Suppress("LeakingThis")
|
||||
@ -195,7 +194,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
val pkToIdCache = PublicKeyToOwningIdentityCacheImpl(database, cacheFactory)
|
||||
@Suppress("LeakingThis")
|
||||
val keyManagementService = makeKeyManagementService(identityService).tokenize()
|
||||
val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersStorage, transactionStorage, bootstrapSource).also {
|
||||
val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersStorage, transactionStorage, bootstrapSource, djvmCordaSource).also {
|
||||
attachments.servicesForResolution = it
|
||||
}
|
||||
@Suppress("LeakingThis")
|
||||
|
@ -1,38 +0,0 @@
|
||||
package net.corda.node.internal
|
||||
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.internal.ContractVerifier
|
||||
import net.corda.core.internal.Verifier
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.djvm.SandboxConfiguration
|
||||
import net.corda.djvm.analysis.AnalysisConfiguration
|
||||
import net.corda.djvm.execution.*
|
||||
import net.corda.djvm.source.ClassSource
|
||||
|
||||
class DeterministicVerifier(
|
||||
ltx: LedgerTransaction,
|
||||
transactionClassLoader: ClassLoader,
|
||||
private val analysisConfiguration: AnalysisConfiguration
|
||||
) : Verifier(ltx, transactionClassLoader) {
|
||||
|
||||
override fun verifyContracts() {
|
||||
try {
|
||||
val configuration = SandboxConfiguration.of(
|
||||
enableTracing = false,
|
||||
analysisConfiguration = analysisConfiguration
|
||||
)
|
||||
val executor = SandboxRawExecutor(configuration)
|
||||
executor.run(ClassSource.fromClassName(ContractVerifier::class.java.name), ltx)
|
||||
} catch (e: Exception) {
|
||||
throw DeterministicVerificationException(ltx.id, e.message ?: "", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
analysisConfiguration.close()
|
||||
}
|
||||
}
|
||||
|
||||
class DeterministicVerificationException(id: SecureHash, message: String, cause: Throwable)
|
||||
: TransactionVerificationException(id, message, cause)
|
@ -4,6 +4,7 @@ import com.codahale.metrics.MetricFilter
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.codahale.metrics.jmx.JmxReporter
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.jcabi.manifests.Manifests
|
||||
import com.palominolabs.metrics.newrelic.AllEnabledMetricAttributeFilter
|
||||
import com.palominolabs.metrics.newrelic.NewRelicReporter
|
||||
import io.netty.util.NettyRuntime
|
||||
@ -30,9 +31,7 @@ import net.corda.core.serialization.internal.SerializationEnvironment
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.djvm.source.ApiSource
|
||||
import net.corda.djvm.source.BootstrapClassLoader
|
||||
import net.corda.djvm.source.EmptyApi
|
||||
import net.corda.djvm.source.*
|
||||
import net.corda.node.CordaClock
|
||||
import net.corda.node.SimpleClock
|
||||
import net.corda.node.VersionInfo
|
||||
@ -78,6 +77,7 @@ import java.lang.Long.max
|
||||
import java.lang.Long.min
|
||||
import java.net.BindException
|
||||
import java.net.InetAddress
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Clock
|
||||
@ -111,13 +111,17 @@ open class Node(configuration: NodeConfiguration,
|
||||
flowManager,
|
||||
// Under normal (non-test execution) it will always be "1"
|
||||
AffinityExecutor.ServiceAffinityExecutor("Node thread-${sameVmNodeCounter.incrementAndGet()}", 1),
|
||||
bootstrapSource = createBootstrapSource(configuration)
|
||||
bootstrapSource = createBootstrapSource(configuration),
|
||||
djvmCordaSource = createDeterministicClasspath(configuration)
|
||||
) {
|
||||
|
||||
override fun createStartedNode(nodeInfo: NodeInfo, rpcOps: CordaRPCOps, notaryService: NotaryService?): NodeInfo =
|
||||
nodeInfo
|
||||
|
||||
companion object {
|
||||
private const val CORDA_DETERMINISTIC_RUNTIME_ATTR = "Corda-Deterministic-Runtime"
|
||||
private const val CORDA_DETERMINISTIC_CLASSPATH_ATTR = "Corda-Deterministic-Classpath"
|
||||
|
||||
private val staticLog = contextLogger()
|
||||
var renderBasicInfoToConsole = true
|
||||
|
||||
@ -177,10 +181,41 @@ open class Node(configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
private fun manifestValue(attrName: String): String? = if (Manifests.exists(attrName)) Manifests.read(attrName) else null
|
||||
|
||||
fun createDeterministicClasspath(config: NodeConfiguration): UserSource? {
|
||||
val classpathSource = config.baseDirectory.resolve("djvm")
|
||||
val djvmClasspath = manifestValue(CORDA_DETERMINISTIC_CLASSPATH_ATTR)
|
||||
|
||||
return if (djvmClasspath == null) {
|
||||
staticLog.warn("{} missing from MANIFEST.MF - deterministic contract verification now impossible!",
|
||||
CORDA_DETERMINISTIC_CLASSPATH_ATTR)
|
||||
null
|
||||
} else if (!Files.isDirectory(classpathSource)) {
|
||||
staticLog.warn("{} directory does not exist - deterministic contract verification now impossible!",
|
||||
classpathSource.toAbsolutePath())
|
||||
null
|
||||
} else {
|
||||
val files = djvmClasspath.split("\\s++".toRegex(), 0).map { classpathSource.resolve(it) }
|
||||
.filter { Files.isRegularFile(it) || Files.isSymbolicLink(it) }
|
||||
staticLog.info("Corda Deterministic Libraries: {}", files.map(Path::getFileName).joinToString())
|
||||
|
||||
val jars = files.map { it.toUri().toURL() }.toTypedArray()
|
||||
UserPathSource(jars)
|
||||
}
|
||||
}
|
||||
|
||||
fun createBootstrapSource(config: NodeConfiguration): ApiSource {
|
||||
val bootstrapSource = config.baseDirectory.resolve("djvm").resolve("deterministic-rt.jar")
|
||||
val deterministicRt = manifestValue(CORDA_DETERMINISTIC_RUNTIME_ATTR)
|
||||
if (deterministicRt == null) {
|
||||
staticLog.warn("{} missing from MANIFEST.MF - will use host JVM for deterministic runtime.",
|
||||
CORDA_DETERMINISTIC_RUNTIME_ATTR)
|
||||
return EmptyApi
|
||||
}
|
||||
|
||||
val bootstrapSource = config.baseDirectory.resolve("djvm").resolve(deterministicRt)
|
||||
return if (bootstrapSource.isRegularFile()) {
|
||||
staticLog.info("Deterministic Runtime: {}", bootstrapSource)
|
||||
staticLog.info("Deterministic Runtime: {}", bootstrapSource.fileName)
|
||||
BootstrapClassLoader(bootstrapSource)
|
||||
} else {
|
||||
staticLog.warn("NO DETERMINISTIC RUNTIME FOUND - will use host JVM instead.")
|
||||
|
@ -19,6 +19,8 @@ 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.URLClassLoader
|
||||
|
||||
data class ServicesForResolutionImpl(
|
||||
@ -27,7 +29,8 @@ data class ServicesForResolutionImpl(
|
||||
override val cordappProvider: CordappProvider,
|
||||
override val networkParametersService: NetworkParametersService,
|
||||
private val validatedTransactions: TransactionStorage,
|
||||
private val djvmBootstrapSource: ApiSource
|
||||
private val djvmBootstrapSource: ApiSource,
|
||||
private val djvmCordaSource: UserSource?
|
||||
) : ServicesForResolution {
|
||||
override val networkParameters: NetworkParameters get() = networkParametersService.lookup(networkParametersService.currentHash) ?:
|
||||
throw IllegalArgumentException("No current parameters in network parameters storage")
|
||||
@ -79,18 +82,21 @@ data class ServicesForResolutionImpl(
|
||||
}
|
||||
|
||||
override fun specialise(ltx: LedgerTransaction): LedgerTransaction {
|
||||
// Do nothing unless we have Corda's deterministic libraries.
|
||||
val cordaSource = djvmCordaSource ?: return ltx
|
||||
|
||||
// Specialise the LedgerTransaction here so that
|
||||
// contracts are verified inside the DJVM!
|
||||
return ltx.specialise { tx, cl ->
|
||||
(cl as? URLClassLoader)?.run { DeterministicVerifier(tx, cl, createSandbox(cl)) } ?: Verifier(tx, cl)
|
||||
(cl as? URLClassLoader)?.run { DeterministicVerifier(tx, cl, createSandbox(cordaSource, cl)) } ?: Verifier(tx, cl)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSandbox(classLoader: URLClassLoader): AnalysisConfiguration {
|
||||
private fun createSandbox(cordaSource: UserSource, classLoader: URLClassLoader): AnalysisConfiguration {
|
||||
return AnalysisConfiguration.createRoot(
|
||||
userSource = UserPathSource(classLoader.urLs),
|
||||
userSource = cordaSource,
|
||||
whitelist = Whitelist.MINIMAL,
|
||||
bootstrapSource = djvmBootstrapSource
|
||||
)
|
||||
).createChild(UserPathSource(classLoader.urLs), null)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package net.corda.node.internal.djvm
|
||||
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.internal.ContractVerifier
|
||||
import net.corda.core.internal.Verifier
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.djvm.SandboxConfiguration
|
||||
import net.corda.djvm.analysis.AnalysisConfiguration
|
||||
import net.corda.djvm.execution.*
|
||||
import net.corda.djvm.messages.Message
|
||||
import net.corda.djvm.source.ClassSource
|
||||
|
||||
class DeterministicVerifier(
|
||||
ltx: LedgerTransaction,
|
||||
transactionClassLoader: ClassLoader,
|
||||
private val analysisConfiguration: AnalysisConfiguration
|
||||
) : Verifier(ltx, transactionClassLoader) {
|
||||
|
||||
override fun verifyContracts() {
|
||||
val configuration = SandboxConfiguration.of(
|
||||
enableTracing = false,
|
||||
analysisConfiguration = analysisConfiguration
|
||||
)
|
||||
val verifierClass = ClassSource.fromClassName(ContractVerifier::class.java.name)
|
||||
val result = IsolatedTask(verifierClass.qualifiedClassName, configuration).run {
|
||||
val executor = Executor(classLoader)
|
||||
|
||||
val sandboxTx = ltx.transform { componentGroups, serializedInputs, serializedReferences ->
|
||||
}
|
||||
|
||||
val verifier = classLoader.loadClassForSandbox(verifierClass).newInstance()
|
||||
|
||||
// Now execute the contract verifier task within the sandbox...
|
||||
executor.execute(verifier, sandboxTx)
|
||||
}
|
||||
|
||||
result.exception?.run {
|
||||
val sandboxEx = SandboxException(
|
||||
Message.getMessageFromException(this),
|
||||
result.identifier,
|
||||
verifierClass,
|
||||
ExecutionSummary(result.costs),
|
||||
this
|
||||
)
|
||||
throw DeterministicVerificationException(ltx.id, sandboxEx.message ?: "", sandboxEx)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
analysisConfiguration.close()
|
||||
}
|
||||
}
|
||||
|
||||
class DeterministicVerificationException(id: SecureHash, message: String, cause: Throwable)
|
||||
: TransactionVerificationException(id, message, cause)
|
@ -0,0 +1,24 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -431,7 +431,7 @@ open class MockServices private constructor(
|
||||
override var networkParametersService: NetworkParametersService = MockNetworkParametersStorage(initialNetworkParameters)
|
||||
|
||||
protected val servicesForResolution: ServicesForResolution
|
||||
get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersService, validatedTransactions, EmptyApi)
|
||||
get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersService, validatedTransactions, EmptyApi, null)
|
||||
|
||||
internal fun makeVaultService(schemaService: SchemaService, database: CordaPersistence, cordappLoader: CordappLoader): VaultServiceInternal {
|
||||
return NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService, cordappLoader.appClassLoader).apply { start() }
|
||||
|
Loading…
x
Reference in New Issue
Block a user