mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge branch 'master' into tudor_merge_os_24_10
# Conflicts: # core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt # core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt # core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt # core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt # core/src/test/kotlin/net/corda/core/contracts/PackageOwnershipVerificationTests.kt # core/src/test/kotlin/net/corda/core/internal/JarSignatureCollectorTest.kt # node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt # node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt # testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TestDSL.kt # testing/test-utils/src/main/kotlin/net/corda/testing/dsl/TransactionDSLInterpreter.kt # testing/test-utils/src/main/kotlin/net/corda/testing/internal/MockCordappProvider.kt
This commit is contained in:
commit
29a8c153ed
@ -564,7 +564,7 @@ public final class net.corda.core.contracts.ContractsDSL extends java.lang.Objec
|
||||
public static final java.util.List<net.corda.core.contracts.CommandWithParties<C>> select(java.util.Collection<? extends net.corda.core.contracts.CommandWithParties<? extends net.corda.core.contracts.CommandData>>, Class<C>, java.util.Collection<? extends java.security.PublicKey>, java.util.Collection<net.corda.core.identity.Party>)
|
||||
##
|
||||
@CordaSerializable
|
||||
public interface net.corda.core.contracts.FungibleAsset extends net.corda.core.contracts.OwnableState
|
||||
public interface net.corda.core.contracts.FungibleAsset extends net.corda.core.contracts.FungibleState, net.corda.core.contracts.OwnableState
|
||||
@NotNull
|
||||
public abstract net.corda.core.contracts.Amount<net.corda.core.contracts.Issued<T>> getAmount()
|
||||
@NotNull
|
||||
@ -572,6 +572,11 @@ public interface net.corda.core.contracts.FungibleAsset extends net.corda.core.c
|
||||
@NotNull
|
||||
public abstract net.corda.core.contracts.FungibleAsset<T> withNewOwnerAndAmount(net.corda.core.contracts.Amount<net.corda.core.contracts.Issued<T>>, net.corda.core.identity.AbstractParty)
|
||||
##
|
||||
@CordaSerializable
|
||||
public interface net.corda.core.contracts.FungibleState extends net.corda.core.contracts.ContractState
|
||||
@NotNull
|
||||
public abstract net.corda.core.contracts.Amount<T> getAmount()
|
||||
##
|
||||
@DoNotImplement
|
||||
@CordaSerializable
|
||||
public final class net.corda.core.contracts.HashAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
|
||||
@ -3369,7 +3374,7 @@ public interface net.corda.core.node.services.VaultService
|
||||
public abstract net.corda.core.messaging.DataFeed<net.corda.core.node.services.Vault$Page<T>, net.corda.core.node.services.Vault$Update<T>> trackBy(Class<? extends T>, net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.Sort)
|
||||
@Suspendable
|
||||
@NotNull
|
||||
public abstract java.util.List<net.corda.core.contracts.StateAndRef<T>> tryLockFungibleStatesForSpending(java.util.UUID, net.corda.core.node.services.vault.QueryCriteria, net.corda.core.contracts.Amount<U>, Class<? extends T>)
|
||||
public abstract java.util.List<net.corda.core.contracts.StateAndRef<T>> tryLockFungibleStatesForSpending(java.util.UUID, net.corda.core.node.services.vault.QueryCriteria, net.corda.core.contracts.Amount<?>, Class<? extends T>)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.core.node.services.Vault$Update<net.corda.core.contracts.ContractState>> whenConsumed(net.corda.core.contracts.StateRef)
|
||||
##
|
||||
@ -5825,8 +5830,6 @@ public interface net.corda.testing.driver.DriverDSL
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>, boolean)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle)
|
||||
@NotNull
|
||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle, String)
|
||||
@ -5835,10 +5838,7 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
|
||||
public <init>()
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public final boolean component1()
|
||||
@NotNull
|
||||
public final java.util.List<String> component10()
|
||||
@ -6031,75 +6031,6 @@ public static final class net.corda.testing.driver.PortAllocation$Incremental ex
|
||||
public final java.util.concurrent.atomic.AtomicInteger getPortCounter()
|
||||
public int nextPort()
|
||||
##
|
||||
@DoNotImplement
|
||||
public interface net.corda.testing.driver.TestCorDapp
|
||||
@NotNull
|
||||
public abstract java.util.Set<Class<?>> getClasses()
|
||||
@NotNull
|
||||
public abstract String getName()
|
||||
@NotNull
|
||||
public abstract java.util.Set<java.net.URL> getResources()
|
||||
@NotNull
|
||||
public abstract String getTitle()
|
||||
@NotNull
|
||||
public abstract String getVendor()
|
||||
@NotNull
|
||||
public abstract String getVersion()
|
||||
@NotNull
|
||||
public abstract java.nio.file.Path packageAsJarInDirectory(java.nio.file.Path)
|
||||
public abstract void packageAsJarWithPath(java.nio.file.Path)
|
||||
##
|
||||
public static final class net.corda.testing.driver.TestCorDapp$Factory extends java.lang.Object
|
||||
public <init>()
|
||||
@NotNull
|
||||
public static final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
||||
public static final net.corda.testing.driver.TestCorDapp$Factory$Companion Companion
|
||||
##
|
||||
public static final class net.corda.testing.driver.TestCorDapp$Factory$Companion extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
||||
##
|
||||
@DoNotImplement
|
||||
public static interface net.corda.testing.driver.TestCorDapp$Mutable extends net.corda.testing.driver.TestCorDapp
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minus(Class<?>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(Package)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(Package, Package...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(String, String...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(java.util.Set<String>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusResource(String, java.net.URL)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plus(Class<?>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(Package)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(Package, Package...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(String, String...)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(java.util.Set<String>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusResource(String, java.net.URL)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withClasses(java.util.Set<? extends Class<?>>)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withName(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withTitle(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVendor(String)
|
||||
@NotNull
|
||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVersion(String)
|
||||
##
|
||||
public final class net.corda.testing.driver.VerifierType extends java.lang.Enum
|
||||
protected <init>()
|
||||
public static net.corda.testing.driver.VerifierType valueOf(String)
|
||||
@ -6241,9 +6172,9 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters)
|
||||
@NotNull
|
||||
@ -6259,9 +6190,9 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters)
|
||||
@NotNull
|
||||
@ -6342,8 +6273,7 @@ public final class net.corda.testing.node.MockNetworkParameters extends java.lan
|
||||
public final class net.corda.testing.node.MockNodeParameters extends java.lang.Object
|
||||
public <init>()
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
@Nullable
|
||||
public final Integer component1()
|
||||
@Nullable
|
||||
@ -6355,9 +6285,7 @@ public final class net.corda.testing.node.MockNodeParameters extends java.lang.O
|
||||
@NotNull
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||
@NotNull
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||
public boolean equals(Object)
|
||||
@NotNull
|
||||
public final kotlin.jvm.functions.Function1<net.corda.node.services.config.NodeConfiguration, Object> getConfigOverrides()
|
||||
|
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@ -134,6 +134,8 @@
|
||||
<module name="loadtest_test" target="1.8" />
|
||||
<module name="mock_main" target="1.8" />
|
||||
<module name="mock_test" target="1.8" />
|
||||
<module name="net.corda-verifier_main" target="1.8" />
|
||||
<module name="net.corda-verifier_test" target="1.8" />
|
||||
<module name="net.corda_buildSrc_main" target="1.8" />
|
||||
<module name="net.corda_buildSrc_test" target="1.8" />
|
||||
<module name="net.corda_canonicalizer_main" target="1.8" />
|
||||
@ -157,8 +159,12 @@
|
||||
<module name="node_main" target="1.8" />
|
||||
<module name="node_smokeTest" target="1.8" />
|
||||
<module name="node_test" target="1.8" />
|
||||
<module name="notary-bft-smart_main" target="1.8" />
|
||||
<module name="notary-bft-smart_test" target="1.8" />
|
||||
<module name="notary-demo_main" target="1.8" />
|
||||
<module name="notary-demo_test" target="1.8" />
|
||||
<module name="notary-raft_main" target="1.8" />
|
||||
<module name="notary-raft_test" target="1.8" />
|
||||
<module name="publish-utils_main" target="1.8" />
|
||||
<module name="publish-utils_test" target="1.8" />
|
||||
<module name="quasar-hook_main" target="1.8" />
|
||||
|
@ -180,7 +180,7 @@ see changes to this list.
|
||||
* Scott James
|
||||
* Sean Zhang (Wells Fargo)
|
||||
* Shams Asari (R3)
|
||||
* Shivan Sawant (Persistent Systems Limited)
|
||||
* Shivan Sawant
|
||||
* Siddhartha Sengupta (Tradewind Markets)
|
||||
* Simon Taylor (Barclays)
|
||||
* Sofus Mortensen (Digital Asset Holdings)
|
||||
|
11
build.gradle
11
build.gradle
@ -5,7 +5,6 @@ buildscript {
|
||||
|
||||
// Our version: bump this on release.
|
||||
ext.corda_release_version = "4.0-SNAPSHOT"
|
||||
// Increment this on any release that changes public APIs anywhere in the Corda platform
|
||||
ext.corda_platform_version = constants.getProperty("platformVersion")
|
||||
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
|
||||
|
||||
@ -50,7 +49,7 @@ buildscript {
|
||||
ext.rxjava_version = '1.3.8'
|
||||
ext.dokka_version = '0.9.17'
|
||||
ext.eddsa_version = '0.2.0'
|
||||
ext.dependency_checker_version = '3.1.0'
|
||||
ext.dependency_checker_version = '3.3.2'
|
||||
ext.commons_collections_version = '4.1'
|
||||
ext.beanutils_version = '1.9.3'
|
||||
ext.crash_version = 'cadb53544fbb3c0fb901445da614998a6a419488'
|
||||
@ -69,9 +68,9 @@ buildscript {
|
||||
ext.commons_cli_version = '1.4'
|
||||
ext.protonj_version = '0.27.1' // This is now aligned with the Artemis version, but retaining in case we ever need to diverge again for a bug fix.
|
||||
ext.snappy_version = '0.4'
|
||||
ext.fast_classpath_scanner_version = '2.12.3'
|
||||
ext.class_graph_version = '4.2.12'
|
||||
ext.jcabi_manifests_version = '1.1'
|
||||
ext.picocli_version = '3.5.2'
|
||||
ext.picocli_version = '3.6.1'
|
||||
|
||||
// Name of the IntelliJ SDK created for the deterministic Java rt.jar.
|
||||
// ext.deterministic_idea_sdk = '1.8 (Deterministic)'
|
||||
@ -362,7 +361,9 @@ bintrayConfig {
|
||||
'corda-tools-blob-inspector',
|
||||
'corda-tools-explorer',
|
||||
'corda-tools-network-bootstrapper',
|
||||
'corda-tools-cliutils'
|
||||
'corda-tools-cliutils',
|
||||
'corda-notary-raft',
|
||||
'corda-notary-bft-smart'
|
||||
]
|
||||
license {
|
||||
name = 'Apache-2.0'
|
||||
|
@ -9,6 +9,7 @@ buildCache {
|
||||
enabled = !isCiServer
|
||||
}
|
||||
remote(HttpBuildCache) {
|
||||
enabled = isCiServer
|
||||
url = gradleBuildCacheURL
|
||||
push = isCiServer
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier()
|
||||
val ctor = constructorForDeserialization(beanClass)
|
||||
val amqpProperties = propertiesForSerialization(ctor, beanClass, serializerFactory)
|
||||
.serializationOrder
|
||||
.map { it.serializer.name }
|
||||
.mapNotNull { if (it.isCalculated) null else it.serializer.name }
|
||||
val propertyRenames = beanDesc.findProperties().associateBy({ it.name }, { it.internalName })
|
||||
(amqpProperties - propertyRenames.values).let {
|
||||
check(it.isEmpty()) { "Jackson didn't provide serialisers for $it" }
|
||||
|
@ -7,7 +7,6 @@ import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import net.corda.client.jfx.utils.*
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.internal.buildNamed
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
import java.security.PublicKey
|
||||
@ -32,7 +31,7 @@ class NetworkIdentityModel {
|
||||
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
|
||||
|
||||
private val identityCache = Caffeine.newBuilder()
|
||||
.buildNamed<PublicKey, ObservableValue<NodeInfo?>>("NetworkIdentityModel_identity", CacheLoader { publicKey: PublicKey ->
|
||||
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader { publicKey: PublicKey ->
|
||||
publicKey.let { rpcProxy.map { it?.cordaRPCOps?.nodeInfoFromParty(AnonymousParty(publicKey)) } }
|
||||
})
|
||||
val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.cordaRPCOps?.notaryIdentities() ?: emptyList()) }, "notaries")
|
||||
|
@ -13,7 +13,7 @@ import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.rpcConnectorTcpTransport
|
||||
import net.corda.nodeapi.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
||||
import java.time.Duration
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
package net.corda.client.rpc.internal
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
|
||||
class ClientCacheFactory : NamedCacheFactory {
|
||||
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V> {
|
||||
checkCacheName(name)
|
||||
return caffeine.build<K, V>()
|
||||
}
|
||||
|
||||
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> {
|
||||
checkCacheName(name)
|
||||
return caffeine.build<K, V>(loader)
|
||||
}
|
||||
}
|
@ -83,7 +83,8 @@ class RPCClientProxyHandler(
|
||||
private val sessionId: Trace.SessionId,
|
||||
private val externalTrace: Trace?,
|
||||
private val impersonatedActor: Actor?,
|
||||
private val targetLegalIdentity: CordaX500Name?
|
||||
private val targetLegalIdentity: CordaX500Name?,
|
||||
private val cacheFactory: NamedCacheFactory = ClientCacheFactory()
|
||||
) : InvocationHandler {
|
||||
|
||||
private enum class State {
|
||||
@ -169,8 +170,7 @@ class RPCClientProxyHandler(
|
||||
}
|
||||
observablesToReap.locked { observables.add(observableId) }
|
||||
}
|
||||
return Caffeine.newBuilder().
|
||||
weakValues().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).buildNamed("RpcClientProxyHandler_rpcObservable")
|
||||
return cacheFactory.buildNamed(Caffeine.newBuilder().weakValues().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()), "RpcClientProxyHandler_rpcObservable")
|
||||
}
|
||||
|
||||
private var sessionFactory: ClientSessionFactory? = null
|
||||
@ -179,7 +179,7 @@ class RPCClientProxyHandler(
|
||||
private var rpcProducer: ClientProducer? = null
|
||||
private var rpcConsumer: ClientConsumer? = null
|
||||
|
||||
private val deduplicationChecker = DeduplicationChecker(rpcConfiguration.deduplicationCacheExpiry)
|
||||
private val deduplicationChecker = DeduplicationChecker(rpcConfiguration.deduplicationCacheExpiry, cacheFactory = cacheFactory)
|
||||
private val deduplicationSequenceNumber = AtomicLong(0)
|
||||
|
||||
private val sendingEnabled = AtomicBoolean(true)
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationContext.*
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.serialization.internal.*
|
||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||
@ -35,7 +34,7 @@ class AMQPClientSerializationScheme(
|
||||
}
|
||||
|
||||
fun createSerializationEnv(classLoader: ClassLoader? = null): SerializationEnvironment {
|
||||
return SerializationEnvironmentImpl(
|
||||
return SerializationEnvironment.with(
|
||||
SerializationFactoryImpl().apply {
|
||||
registerScheme(AMQPClientSerializationScheme(emptyList()))
|
||||
},
|
||||
|
@ -20,7 +20,6 @@ dependencies {
|
||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
||||
|
||||
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
||||
testCompile project(":node")
|
||||
testCompile project(":node-driver")
|
||||
|
||||
// AssertJ: for fluent assertions for testing
|
||||
|
@ -1,7 +1,9 @@
|
||||
gradlePluginsVersion=4.0.29
|
||||
kotlinVersion=1.2.51
|
||||
# When adjusting platformVersion upwards please also modify CordaRPCClientConfiguration.minimumServerProtocolVersion \
|
||||
# if there have been any RPC changes. Also please modify InternalMockNetwork.kt:MOCK_VERSION_INFO and NodeBasedTest.startNode
|
||||
gradlePluginsVersion=4.0.33
|
||||
kotlinVersion=1.2.71
|
||||
# ***************************************************************#
|
||||
# When incrementing platformVersion make sure to update #
|
||||
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
|
||||
# ***************************************************************#
|
||||
platformVersion=4
|
||||
guavaVersion=25.1-jre
|
||||
proguardVersion=6.0.3
|
||||
|
@ -1,74 +0,0 @@
|
||||
package net.corda.core.serialization.internal
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import java.io.NotSerializableException
|
||||
|
||||
/**
|
||||
* A deterministic version of [CheckpointSerializationFactory] that does not use thread-locals to manage serialization
|
||||
* context.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class CheckpointSerializationFactory(
|
||||
private val scheme: CheckpointSerializationScheme
|
||||
) {
|
||||
|
||||
val defaultContext: CheckpointSerializationContext get() = _currentContext ?: effectiveSerializationEnv.checkpointContext
|
||||
|
||||
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
|
||||
|
||||
/**
|
||||
* Deserialize the bytes in to an object, using the prefixed bytes to determine the format.
|
||||
*
|
||||
* @param byteSequence The bytes to deserialize, including a format header prefix.
|
||||
* @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown.
|
||||
* @param context A context that configures various parameters to deserialization.
|
||||
*/
|
||||
@Throws(NotSerializableException::class)
|
||||
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T {
|
||||
return withCurrentContext(context) { scheme.deserialize(byteSequence, clazz, context) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an object to bytes using the preferred serialization format version from the context.
|
||||
*
|
||||
* @param obj The object to be serialized.
|
||||
* @param context A context that configures various parameters to serialization, including the serialization format version.
|
||||
*/
|
||||
fun <T : Any> serialize(obj: T, context: CheckpointSerializationContext): SerializedBytes<T> {
|
||||
return withCurrentContext(context) { scheme.serialize(obj, context) }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${this.javaClass.name} scheme=$scheme ${creator.joinToString("\n")}"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CheckpointSerializationFactory && other.scheme == this.scheme
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = scheme.hashCode()
|
||||
|
||||
private var _currentContext: CheckpointSerializationContext? = null
|
||||
|
||||
/**
|
||||
* Change the current context inside the block to that supplied.
|
||||
*/
|
||||
fun <T> withCurrentContext(context: CheckpointSerializationContext?, block: () -> T): T {
|
||||
val priorContext = _currentContext
|
||||
if (context != null) _currentContext = context
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
if (context != null) _currentContext = priorContext
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* A default factory for serialization/deserialization.
|
||||
*/
|
||||
val defaultFactory: CheckpointSerializationFactory get() = effectiveSerializationEnv.checkpointSerializationFactory
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@ test {
|
||||
// Running this class is the whole point, so include it explicitly.
|
||||
includeTestsMatching "net.corda.deterministic.data.GenerateData"
|
||||
}
|
||||
// force execution of these tests to generate artifacts required by other module (eg. VerifyTransactionTest)
|
||||
// note: required by Gradle Build Cache.
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
assemble.finalizedBy test
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
@file:JvmName("CryptoSignUtils")
|
||||
|
||||
package net.corda.deterministic.crypto
|
||||
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.Crypto.findSignatureScheme
|
||||
import net.corda.core.crypto.Crypto.isSupportedSignatureScheme
|
||||
import net.corda.core.serialization.serialize
|
||||
import java.security.*
|
||||
|
||||
/**
|
||||
* This is a slightly modified copy of signing utils from net.corda.core.crypto.Crypto, which are normally removed from DJVM.
|
||||
* However, we need those for TransactionSignatureTest.
|
||||
*/
|
||||
object CryptoSignUtils {
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
|
||||
return doSign(findSignatureScheme(schemeCodeName), privateKey, clearData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature].
|
||||
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
|
||||
* @param privateKey the signer's [PrivateKey].
|
||||
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
|
||||
* @return the digital signature (in [ByteArray]) on the input message.
|
||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
|
||||
require(isSupportedSignatureScheme(signatureScheme)) {
|
||||
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
|
||||
}
|
||||
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
|
||||
val signature = Signature.getInstance(signatureScheme.signatureName, signatureScheme.providerName)
|
||||
signature.initSign(privateKey)
|
||||
signature.update(clearData)
|
||||
return signature.sign()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic way to sign [SignableData] objects with a [PrivateKey].
|
||||
* [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as
|
||||
* a timestamp or partial and blind signature indicators.
|
||||
* @param keyPair the signer's [KeyPair].
|
||||
* @param signableData a [SignableData] object that adds extra information to a transaction.
|
||||
* @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and
|
||||
* the signature metadata.
|
||||
* @throws IllegalArgumentException if the signature scheme is not supported for this private key.
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
|
||||
val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
|
||||
val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
|
||||
require(sigKey == sigMetaData) {
|
||||
"Metadata schemeCodeName: ${sigMetaData.schemeCodeName} is not aligned with the key type: ${sigKey.schemeCodeName}."
|
||||
}
|
||||
val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes)
|
||||
return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata)
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ class TransactionSignatureTest {
|
||||
|
||||
// Sign the meta object.
|
||||
val transactionSignature: TransactionSignature = CheatingSecurityProvider().use {
|
||||
keyPair.sign(signableData)
|
||||
CryptoSignUtils.doSign(keyPair, signableData)
|
||||
}
|
||||
|
||||
// Check auto-verification.
|
||||
@ -52,7 +52,7 @@ class TransactionSignatureTest {
|
||||
fun `Signature metadata full failure clearData has changed`() {
|
||||
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||
val transactionSignature = CheatingSecurityProvider().use {
|
||||
keyPair.sign(signableData)
|
||||
CryptoSignUtils.doSign(keyPair, signableData)
|
||||
}
|
||||
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
|
||||
}
|
||||
@ -137,7 +137,7 @@ class TransactionSignatureTest {
|
||||
private fun signOneTx(txId: SecureHash, keyPair: KeyPair): TransactionSignature {
|
||||
val signableData = SignableData(txId, SignatureMetadata(3, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
|
||||
return CheatingSecurityProvider().use {
|
||||
keyPair.sign(signableData)
|
||||
CryptoSignUtils.doSign(keyPair, signableData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import net.corda.core.serialization.ClassWhitelist
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationContext.UseCase.P2P
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||
import net.corda.serialization.internal.*
|
||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||
@ -58,13 +58,11 @@ class LocalSerializationRule(private val label: String) : TestRule {
|
||||
_contextSerializationEnv.set(null)
|
||||
}
|
||||
|
||||
private fun createTestSerializationEnv(): SerializationEnvironmentImpl {
|
||||
private fun createTestSerializationEnv(): SerializationEnvironment {
|
||||
val factory = SerializationFactoryImpl(mutableMapOf()).apply {
|
||||
registerScheme(AMQPSerializationScheme(emptySet(), AccessOrderLinkedHashMap(128)))
|
||||
}
|
||||
return object : SerializationEnvironmentImpl(factory, AMQP_P2P_CONTEXT) {
|
||||
override fun toString() = "testSerializationEnv($label)"
|
||||
}
|
||||
return SerializationEnvironment.with(factory, AMQP_P2P_CONTEXT)
|
||||
}
|
||||
|
||||
private class AMQPSerializationScheme(
|
||||
|
@ -61,7 +61,6 @@ dependencies {
|
||||
testCompile "com.google.guava:guava-testlib:$guava_version"
|
||||
|
||||
// Bring in the MockNode infrastructure for writing protocol unit tests.
|
||||
testCompile project(":node")
|
||||
testCompile project(":node-driver")
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.extractFile
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.io.FileNotFoundException
|
||||
|
@ -3,6 +3,7 @@ package net.corda.core.contracts
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.serialization.SerializableCalculatedProperty
|
||||
import java.security.PublicKey
|
||||
|
||||
/**
|
||||
@ -27,17 +28,18 @@ class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException
|
||||
* (GBP, USD, oil, shares in company <X>, etc.) and any additional metadata (issuer, grade, class, etc.).
|
||||
*/
|
||||
@KeepForDJVM
|
||||
interface FungibleAsset<T : Any> : OwnableState {
|
||||
interface FungibleAsset<T : Any> : FungibleState<Issued<T>>, OwnableState {
|
||||
/**
|
||||
* Amount represents a positive quantity of some issued product which can be cash, tokens, assets, or generally
|
||||
* anything else that's quantifiable with integer quantities. See [Issued] and [Amount] for more details.
|
||||
*/
|
||||
val amount: Amount<Issued<T>>
|
||||
override val amount: Amount<Issued<T>>
|
||||
|
||||
/**
|
||||
* There must be an ExitCommand signed by these keys to destroy the amount. While all states require their
|
||||
* owner to sign, some (i.e. cash) also require the issuer.
|
||||
*/
|
||||
@get:SerializableCalculatedProperty
|
||||
val exitKeys: Collection<PublicKey>
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
|
||||
/**
|
||||
* Interface to represent things which are fungible, this means that there is an expectation that these things can
|
||||
* be split and merged. That's the only assumption made by this interface.
|
||||
*
|
||||
* This interface has been defined in addition to [FungibleAsset] to provide some additional flexibility which
|
||||
* [FungibleAsset] lacks, in particular:
|
||||
*
|
||||
* - [FungibleAsset] defines an amount property of type Amount<Issued<T>>, therefore there is an assumption that all
|
||||
* fungible things are issued by a single well known party but this is not always the case. For example,
|
||||
* crypto-currencies like Bitcoin are generated periodically by a pool of pseudo-anonymous miners
|
||||
* and Corda can support such crypto-currencies.
|
||||
* - [FungibleAsset] implements [OwnableState], as such there is an assumption that all fungible things are ownable.
|
||||
* This is not always true as fungible derivative contracts exist, for example.
|
||||
*
|
||||
* The expectation is that this interface should be combined with the other core state interfaces such as
|
||||
* [OwnableState] and others created at the application layer.
|
||||
*
|
||||
* @param T a type that represents the fungible thing in question. This should describe the basic type of the asset
|
||||
* (GBP, USD, oil, shares in company <X>, etc.) and any additional metadata (issuer, grade, class, etc.). An
|
||||
* upper-bound is not specified for [T] to ensure flexibility. Typically, a class would be provided that implements
|
||||
* [TokenizableAssetInfo].
|
||||
*/
|
||||
// DOCSTART 1
|
||||
@KeepForDJVM
|
||||
interface FungibleState<T : Any> : ContractState {
|
||||
/**
|
||||
* Amount represents a positive quantity of some token which can be cash, tokens, stock, agreements, or generally
|
||||
* anything else that's quantifiable with integer quantities. See [Amount] for more details.
|
||||
*/
|
||||
val amount: Amount<T>
|
||||
}
|
||||
// DOCEND 1
|
||||
|
@ -130,13 +130,32 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
class TransactionMissingEncumbranceException(txId: SecureHash, val missing: Int, val inOut: Direction)
|
||||
: TransactionVerificationException(txId, "Missing required encumbrance $missing in $inOut", null)
|
||||
|
||||
/**
|
||||
* If two or more states refer to another state (as their encumbrance), then the bi-directionality property cannot
|
||||
* be satisfied.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionDuplicateEncumbranceException(txId: SecureHash, index: Int)
|
||||
: TransactionVerificationException(txId, "The bi-directionality property of encumbered output states " +
|
||||
"is not satisfied. Index $index is referenced more than once", null)
|
||||
|
||||
/**
|
||||
* An encumbered state should also be referenced as the encumbrance of another state in order to satisfy the
|
||||
* bi-directionality property (a full cycle should be present).
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionNonMatchingEncumbranceException(txId: SecureHash, nonMatching: Collection<Int>)
|
||||
: TransactionVerificationException(txId, "The bi-directionality property of encumbered output states " +
|
||||
"is not satisfied. Encumbered states should also be referenced as an encumbrance of another state to form " +
|
||||
"a full cycle. Offending indices $nonMatching", null)
|
||||
|
||||
/** Whether the inputs or outputs list contains an encumbrance issue, see [TransactionMissingEncumbranceException]. */
|
||||
@CordaSerializable
|
||||
@KeepForDJVM
|
||||
enum class Direction {
|
||||
/** Issue in the inputs list */
|
||||
/** Issue in the inputs list. */
|
||||
INPUT,
|
||||
/** Issue in the outputs list */
|
||||
/** Issue in the outputs list. */
|
||||
OUTPUT
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
@ -48,4 +49,5 @@ interface Cordapp {
|
||||
val jarPath: URL
|
||||
val cordappClasses: List<String>
|
||||
val jarHash: SecureHash.SHA256
|
||||
val notaryService: Class<out NotaryService>?
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.StubOutForDJVM
|
||||
import net.corda.core.crypto.internal.*
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine
|
||||
@ -23,6 +24,7 @@ import org.bouncycastle.asn1.sec.SECObjectIdentifiers
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers
|
||||
import org.bouncycastle.crypto.CryptoServicesRegistrar
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey
|
||||
@ -381,6 +383,7 @@ object Crypto {
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray = doSign(findSignatureScheme(privateKey), privateKey, clearData)
|
||||
@ -395,6 +398,7 @@ object Crypto {
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
|
||||
@ -411,6 +415,7 @@ object Crypto {
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
|
||||
@ -419,7 +424,20 @@ object Crypto {
|
||||
}
|
||||
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
|
||||
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
|
||||
signature.initSign(privateKey)
|
||||
// Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require
|
||||
// extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking
|
||||
// SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with
|
||||
// ClassCastException if we invoke initSign with a SecureRandom as an input.
|
||||
// TODO Although we handle the above issue here, consider updating to BC 1.61+ which provides a fix.
|
||||
if (signatureScheme == EDDSA_ED25519_SHA512
|
||||
|| signatureScheme == SPHINCS256_SHA256
|
||||
|| signatureScheme == RSA_SHA256) {
|
||||
signature.initSign(privateKey)
|
||||
} else {
|
||||
// The rest of the algorithms will require a SecureRandom input (i.e., ECDSA or any new algorithm for which
|
||||
// we don't know if it's deterministic).
|
||||
signature.initSign(privateKey, newSecureRandom())
|
||||
}
|
||||
signature.update(clearData)
|
||||
return signature.sign()
|
||||
}
|
||||
@ -436,6 +454,7 @@ object Crypto {
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
|
||||
@ -794,7 +813,6 @@ object Crypto {
|
||||
* @return a new [KeyPair] from an entropy input.
|
||||
* @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||
return when (signatureScheme) {
|
||||
@ -810,7 +828,6 @@ object Crypto {
|
||||
* @param entropy a [BigInteger] value.
|
||||
* @return a new [KeyPair] from an entropy input.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@JvmStatic
|
||||
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
|
||||
|
||||
@ -1017,5 +1034,15 @@ object Crypto {
|
||||
@JvmStatic
|
||||
fun registerProviders() {
|
||||
providerMap
|
||||
// Adding our non-blocking newSecureRandom as default for any BouncyCastle operations
|
||||
// (applies only when a SecureRandom is not specifically defined, i.e., if we call
|
||||
// signature.initSign(privateKey) instead of signature.initSign(privateKey, newSecureRandom()
|
||||
// for a BC algorithm, i.e., ECDSA).
|
||||
setBouncyCastleRNG()
|
||||
}
|
||||
|
||||
@StubOutForDJVM
|
||||
private fun setBouncyCastleRNG() {
|
||||
CryptoServicesRegistrar.setSecureRandom(newSecureRandom())
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.security.*
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature(Crypto.doSign(this, bytesToSign))
|
||||
|
||||
@ -36,6 +37,7 @@ fun PrivateKey.sign(bytesToSign: ByteArray): DigitalSignature = DigitalSignature
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||
return DigitalSignature.WithKey(publicKey, this.sign(bytesToSign).bytes)
|
||||
@ -49,10 +51,12 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey): DigitalSignat
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun KeyPair.sign(bytesToSign: ByteArray): DigitalSignature.WithKey = private.sign(bytesToSign, public)
|
||||
|
||||
/** Helper function to sign the bytes of [bytesToSign] with a key pair. */
|
||||
@DeleteForDJVM
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(bytesToSign.bytes)
|
||||
|
||||
@ -64,6 +68,7 @@ fun KeyPair.sign(bytesToSign: OpaqueBytes): DigitalSignature.WithKey = sign(byte
|
||||
* @throws InvalidKeyException if the private key is invalid.
|
||||
* @throws SignatureException if signing is not possible due to malformed data or private key.
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun KeyPair.sign(signableData: SignableData): TransactionSignature = Crypto.doSign(this, signableData)
|
||||
|
||||
@ -145,7 +150,6 @@ fun generateKeyPair(): KeyPair = Crypto.generateKeyPair()
|
||||
* @param entropy a [BigInteger] value.
|
||||
* @return a deterministically generated [KeyPair] for the [Crypto.DEFAULT_SIGNATURE_SCHEME].
|
||||
*/
|
||||
@DeleteForDJVM
|
||||
fun entropyToKeyPair(entropy: BigInteger): KeyPair = Crypto.deriveKeyPairFromEntropy(entropy)
|
||||
|
||||
/**
|
||||
|
@ -62,6 +62,7 @@ abstract class AbstractStateReplacementFlow {
|
||||
@Throws(StateReplacementException::class)
|
||||
override fun call(): StateAndRef<T> {
|
||||
val (stx) = assembleTx()
|
||||
stx.verify(serviceHub, checkSufficientSignatures = false)
|
||||
val participantSessions = getParticipantSessions()
|
||||
progressTracker.currentStep = SIGNING
|
||||
|
||||
|
@ -64,8 +64,10 @@ abstract class FlowLogic<out T> {
|
||||
/**
|
||||
* Return the outermost [FlowLogic] instance, or null if not in a flow.
|
||||
*/
|
||||
@Suppress("unused") @JvmStatic
|
||||
val currentTopLevel: FlowLogic<*>? get() = (Strand.currentStrand() as? FlowStateMachine<*>)?.logic
|
||||
@Suppress("unused")
|
||||
@JvmStatic
|
||||
val currentTopLevel: FlowLogic<*>?
|
||||
get() = (Strand.currentStrand() as? FlowStateMachine<*>)?.logic
|
||||
|
||||
/**
|
||||
* If on a flow, suspends the flow and only wakes it up after at least [duration] time has passed. Otherwise,
|
||||
@ -123,10 +125,11 @@ abstract class FlowLogic<out T> {
|
||||
* Note: The current implementation returns the single identity of the node. This will change once multiple identities
|
||||
* is implemented.
|
||||
*/
|
||||
val ourIdentityAndCert: PartyAndCertificate get() {
|
||||
return serviceHub.myInfo.legalIdentitiesAndCerts.find { it.party == stateMachine.ourIdentity }
|
||||
?: throw IllegalStateException("Identity specified by ${stateMachine.id} (${stateMachine.ourIdentity}) is not one of ours!")
|
||||
}
|
||||
val ourIdentityAndCert: PartyAndCertificate
|
||||
get() {
|
||||
return serviceHub.myInfo.legalIdentitiesAndCerts.find { it.party == stateMachine.ourIdentity }
|
||||
?: throw IllegalStateException("Identity specified by ${stateMachine.id} (${stateMachine.ourIdentity}) is not one of ours!")
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the identity to use for this flow. This will be one of the multiple identities that belong to this node.
|
||||
@ -141,9 +144,11 @@ abstract class FlowLogic<out T> {
|
||||
// Used to implement the deprecated send/receive functions using Party. When such a deprecated function is used we
|
||||
// create a fresh session for the Party, put it here and use it in subsequent deprecated calls.
|
||||
private val deprecatedPartySessionMap = HashMap<Party, FlowSession>()
|
||||
|
||||
private fun getDeprecatedSessionForParty(party: Party): FlowSession {
|
||||
return deprecatedPartySessionMap.getOrPut(party) { initiateFlow(party) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [FlowInfo] object describing the flow [otherParty] is using. With [FlowInfo.flowVersion] it
|
||||
* provides the necessary information needed for the evolution of flows and enabling backwards compatibility.
|
||||
@ -342,7 +347,7 @@ abstract class FlowLogic<out T> {
|
||||
* Note that this has to return a tracker before the flow is invoked. You can't change your mind half way
|
||||
* through.
|
||||
*/
|
||||
open val progressTracker: ProgressTracker? = null
|
||||
open val progressTracker: ProgressTracker? = ProgressTracker.DEFAULT_TRACKER()
|
||||
|
||||
/**
|
||||
* This is where you fill out your business logic.
|
||||
@ -383,7 +388,7 @@ abstract class FlowLogic<out T> {
|
||||
*
|
||||
* @return Returns null if this flow has no progress tracker.
|
||||
*/
|
||||
fun trackStepsTree(): DataFeed<List<Pair<Int,String>>, List<Pair<Int,String>>>? {
|
||||
fun trackStepsTree(): DataFeed<List<Pair<Int, String>>, List<Pair<Int, String>>>? {
|
||||
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
||||
return progressTracker?.let {
|
||||
DataFeed(it.allStepsLabels, it.stepsTreeChanges)
|
||||
|
@ -46,14 +46,14 @@ class NotaryChangeFlow<out T : ContractState>(
|
||||
return AbstractStateReplacementFlow.UpgradeTx(stx)
|
||||
}
|
||||
|
||||
/** Resolves the encumbrance state chain for the given [state] */
|
||||
/** Resolves the encumbrance state chain for the given [state]. */
|
||||
private fun resolveEncumbrances(state: StateAndRef<T>): List<StateAndRef<T>> {
|
||||
val states = mutableListOf(state)
|
||||
val states = mutableSetOf(state)
|
||||
while (states.last().state.encumbrance != null) {
|
||||
val encumbranceStateRef = StateRef(states.last().ref.txhash, states.last().state.encumbrance!!)
|
||||
val encumbranceState = serviceHub.toStateAndRef<T>(encumbranceStateRef)
|
||||
states.add(encumbranceState)
|
||||
if (!states.add(encumbranceState)) break // Stop if there is a cycle.
|
||||
}
|
||||
return states
|
||||
return states.toList()
|
||||
}
|
||||
}
|
||||
|
@ -26,20 +26,25 @@ import java.security.cert.X509Certificate
|
||||
enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Boolean, val isWellKnown: Boolean) : ASN1Encodable {
|
||||
/** Signing certificate for the Doorman CA. */
|
||||
DOORMAN_CA(NonEmptySet.of(null), false, false),
|
||||
|
||||
/** Signing certificate for the network map. */
|
||||
NETWORK_MAP(NonEmptySet.of(null), false, false),
|
||||
|
||||
/** Well known (publicly visible) identity of a service (such as notary). */
|
||||
SERVICE_IDENTITY(NonEmptySet.of(DOORMAN_CA), true, true),
|
||||
|
||||
/** Node level CA from which the TLS and well known identity certificates are issued. */
|
||||
NODE_CA(NonEmptySet.of(DOORMAN_CA), false, false),
|
||||
|
||||
// [DOORMAN_CA] is also added as a valid parent of [TLS] and [LEGAL_IDENTITY] for backwards compatibility purposes
|
||||
// (eg. if we decide [TLS] has its own [ROOT_CA] and [DOORMAN_CA] directly issues [TLS] and [LEGAL_IDENTITY]; thus,
|
||||
// there won't be a requirement for [NODE_CA]).
|
||||
/** Transport layer security certificate for a node. */
|
||||
TLS(NonEmptySet.of(NODE_CA), false, false),
|
||||
TLS(NonEmptySet.of(DOORMAN_CA, NODE_CA), false, false),
|
||||
|
||||
/** Well known (publicly visible) identity of a legal entity. */
|
||||
// TODO: at the moment, Legal Identity certs are issued by Node CA only. However, [DOORMAN_CA] is also added
|
||||
// as a valid parent of [LEGAL_IDENTITY] for backwards compatibility purposes (eg. if we decide TLS has its
|
||||
// own Root CA and Doorman CA directly issues Legal Identities; thus, there won't be a requirement for
|
||||
// Node CA). Consider removing [DOORMAN_CA] from [validParents] when the model is finalised.
|
||||
LEGAL_IDENTITY(NonEmptySet.of(DOORMAN_CA, NODE_CA), true, true),
|
||||
|
||||
/** Confidential (limited visibility) identity of a legal entity. */
|
||||
CONFIDENTIAL_LEGAL_IDENTITY(NonEmptySet.of(LEGAL_IDENTITY), true, false);
|
||||
|
||||
@ -88,4 +93,4 @@ enum class CertRole(val validParents: NonEmptySet<CertRole?>, val isIdentity: Bo
|
||||
fun isValidParent(parent: CertRole?): Boolean = parent in validParents
|
||||
|
||||
override fun toASN1Primitive(): ASN1Primitive = ASN1Integer(this.ordinal + 1L)
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import org.slf4j.MDC
|
||||
|
||||
// *Internal* Corda-specific utilities
|
||||
|
||||
const val PLATFORM_VERSION = 4
|
||||
|
||||
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
|
||||
val currentMinPlatformVersion = networkParameters.minimumPlatformVersion
|
||||
if (currentMinPlatformVersion < requiredMinPlatformVersion) {
|
||||
|
@ -12,8 +12,14 @@ import net.corda.core.serialization.CordaSerializable
|
||||
*/
|
||||
@CordaSerializable
|
||||
interface FlowAsyncOperation<R : Any> {
|
||||
/** Performs the operation in a non-blocking fashion. */
|
||||
fun execute(): CordaFuture<R>
|
||||
/**
|
||||
* Performs the operation in a non-blocking fashion.
|
||||
* @param deduplicationId If the flow restarts from a checkpoint (due to node restart, or via a visit to the flow
|
||||
* hospital following an error) the execute method might be called more than once by the Corda flow state machine.
|
||||
* For each duplicate call, the deduplicationId is guaranteed to be the same allowing duplicate requests to be
|
||||
* de-duplicated if necessary inside the execute method.
|
||||
*/
|
||||
fun execute(deduplicationId: String): CordaFuture<R>
|
||||
}
|
||||
// DOCEND FlowAsyncOperation
|
||||
|
||||
@ -24,4 +30,4 @@ fun <T, R : Any> FlowLogic<T>.executeAsync(operation: FlowAsyncOperation<R>, may
|
||||
val request = FlowIORequest.ExecuteAsyncOperation(operation)
|
||||
return stateMachine.suspend(request, maySkipCheckpoint)
|
||||
}
|
||||
// DOCEND executeAsync
|
||||
// DOCEND executeAsync
|
||||
|
@ -457,11 +457,13 @@ $trustAnchor""", e, this, e.index)
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteForDJVM
|
||||
inline fun <T : Any> T.signWithCert(signer: (SerializedBytes<T>) -> DigitalSignatureWithCert): SignedDataWithCert<T> {
|
||||
val serialised = serialize()
|
||||
return SignedDataWithCert(serialised, signer(serialised))
|
||||
}
|
||||
|
||||
@DeleteForDJVM
|
||||
fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> {
|
||||
return signWithCert {
|
||||
val signature = Crypto.doSign(privateKey, it.bytes)
|
||||
@ -469,10 +471,12 @@ fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificat
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteForDJVM
|
||||
inline fun <T : Any> SerializedBytes<T>.sign(signer: (SerializedBytes<T>) -> DigitalSignature.WithKey): SignedData<T> {
|
||||
return SignedData(this, signer(this))
|
||||
}
|
||||
|
||||
@DeleteForDJVM
|
||||
fun <T : Any> SerializedBytes<T>.sign(keyPair: KeyPair): SignedData<T> = SignedData(this, keyPair.sign(this.bytes))
|
||||
|
||||
fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) }
|
||||
|
@ -12,8 +12,11 @@ import java.util.jar.JarInputStream
|
||||
*/
|
||||
object JarSignatureCollector {
|
||||
|
||||
/** @see <https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File> */
|
||||
private val unsignableEntryName = "META-INF/(?:.*[.](?:SF|DSA|RSA)|SIG-.*)".toRegex()
|
||||
/**
|
||||
* @see <https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File>
|
||||
* also accepting *.EC as this can be created and accepted by jarsigner tool @see https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jarsigner.html
|
||||
* and Java Security Manager. */
|
||||
private val unsignableEntryName = "META-INF/(?:.*[.](?:SF|DSA|RSA|EC)|SIG-.*)".toRegex()
|
||||
|
||||
/**
|
||||
* Returns an ordered list of every [Party] which has signed every signable item in the given [JarInputStream].
|
||||
@ -21,22 +24,33 @@ object JarSignatureCollector {
|
||||
* @param jar The open [JarInputStream] to collect signing parties from.
|
||||
* @throws InvalidJarSignersException If the signer sets for any two signable items are different from each other.
|
||||
*/
|
||||
fun collectSigners(jar: JarInputStream): List<PublicKey> {
|
||||
fun collectSigners(jar: JarInputStream): List<PublicKey> = getSigners(jar).toOrderedPublicKeys()
|
||||
|
||||
fun collectSigningParties(jar: JarInputStream): List<Party> = getSigners(jar).toPartiesOrderedByName()
|
||||
|
||||
/**
|
||||
* Returns an ordered list of every [X509Certificate] which has signed every signable item in the given [JarInputStream].
|
||||
*
|
||||
* @param jar The open [JarInputStream] to collect signing parties from.
|
||||
* @throws InvalidJarSignersException If the signer sets for any two signable items are different from each other.
|
||||
*/
|
||||
fun collectCertificates(jar: JarInputStream): List<X509Certificate> = getSigners(jar).toCertificates()
|
||||
|
||||
private fun getSigners(jar: JarInputStream): Set<CodeSigner> {
|
||||
val signerSets = jar.fileSignerSets
|
||||
if (signerSets.isEmpty()) return emptyList()
|
||||
if (signerSets.isEmpty()) return emptySet()
|
||||
|
||||
val (firstFile, firstSignerSet) = signerSets.first()
|
||||
for ((otherFile, otherSignerSet) in signerSets.subList(1, signerSets.size)) {
|
||||
if (otherSignerSet != firstSignerSet) throw InvalidJarSignersException(
|
||||
"""
|
||||
Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile
|
||||
and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}.
|
||||
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
||||
constraints applied to attachment signatures.
|
||||
""".trimIndent().replace('\n', ' '))
|
||||
"""
|
||||
Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile
|
||||
and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}.
|
||||
See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the
|
||||
constraints applied to attachment signatures.
|
||||
""".trimIndent().replace('\n', ' '))
|
||||
}
|
||||
|
||||
return firstSignerSet.toOrderedPublicKeys()
|
||||
return firstSignerSet
|
||||
}
|
||||
|
||||
private val JarInputStream.fileSignerSets: List<Pair<String, Set<CodeSigner>>> get() =
|
||||
@ -57,10 +71,18 @@ object JarSignatureCollector {
|
||||
private fun Sequence<JarEntry>.toFileSignerSet(): Sequence<Pair<String, Set<CodeSigner>>> =
|
||||
map { entry -> entry.name to (entry.codeSigners?.toSet() ?: emptySet()) }
|
||||
|
||||
private fun Set<CodeSigner>.toPartiesOrderedByName(): List<Party> = map {
|
||||
Party(it.signerCertPath.certificates[0] as X509Certificate)
|
||||
}.sortedBy { it.name.toString() } // Sorted for determinism.
|
||||
|
||||
private fun Set<CodeSigner>.toOrderedPublicKeys(): List<PublicKey> = map {
|
||||
(it.signerCertPath.certificates[0] as X509Certificate).publicKey
|
||||
}.sortedBy { it.hash} // Sorted for determinism.
|
||||
|
||||
private fun Set<CodeSigner>.toCertificates(): List<X509Certificate> = map {
|
||||
it.signerCertPath.certificates[0] as X509Certificate
|
||||
}.sortedBy { it.toString() } // Sorted for determinism.
|
||||
|
||||
private val JarInputStream.entries get(): Sequence<JarEntry> = generateSequence(nextJarEntry) { nextJarEntry }
|
||||
}
|
||||
|
||||
|
@ -6,30 +6,21 @@ import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
|
||||
/**
|
||||
* Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that
|
||||
* the name can be used to create a file name or a metric name.
|
||||
* Allow extra functionality to be injected to our caches.
|
||||
*/
|
||||
internal fun checkCacheName(name: String) {
|
||||
require(!name.isBlank())
|
||||
require(allowedChars.matches(name))
|
||||
interface NamedCacheFactory {
|
||||
/**
|
||||
* Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that
|
||||
* the name can be used to create a file name or a metric name.
|
||||
*/
|
||||
fun checkCacheName(name: String) {
|
||||
require(!name.isBlank())
|
||||
require(allowedChars.matches(name))
|
||||
}
|
||||
|
||||
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V>
|
||||
|
||||
fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V>
|
||||
}
|
||||
|
||||
private val allowedChars = Regex("^[0-9A-Za-z_.]*\$")
|
||||
|
||||
/* buildNamed is the central helper method to build caffeine caches in Corda.
|
||||
* This allows to easily add tweaks to all caches built in Corda, and also forces
|
||||
* cache users to give their cache a (meaningful) name that can be used e.g. for
|
||||
* capturing cache traces etc.
|
||||
*
|
||||
* Currently it is not used in this version of CORDA, but there are plans to do so.
|
||||
*/
|
||||
|
||||
fun <K, V> Caffeine<in K, in V>.buildNamed(name: String): Cache<K, V> {
|
||||
checkCacheName(name)
|
||||
return this.build<K, V>()
|
||||
}
|
||||
|
||||
fun <K, V> Caffeine<in K, in V>.buildNamed(name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> {
|
||||
checkCacheName(name)
|
||||
return this.build<K, V>(loader)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class WaitForStateConsumption(val stateRefs: Set<StateRef>, val services: Servic
|
||||
val logger = contextLogger()
|
||||
}
|
||||
|
||||
override fun execute(): CordaFuture<Unit> {
|
||||
override fun execute(deduplicationId: String): CordaFuture<Unit> {
|
||||
val futures = stateRefs.map { services.vaultService.whenConsumed(it).toCompletableFuture() }
|
||||
val completedFutures = futures.filter { it.isDone }
|
||||
|
||||
@ -40,4 +40,4 @@ class WaitForStateConsumption(val stateRefs: Set<StateRef>, val services: Servic
|
||||
|
||||
return CompletableFuture.allOf(*futures.toTypedArray()).thenApply { Unit }.asCordaFuture()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
@ -25,7 +26,10 @@ data class CordappImpl(
|
||||
override val allFlows: List<Class<out FlowLogic<*>>>,
|
||||
override val jarPath: URL,
|
||||
val info: Info,
|
||||
override val jarHash: SecureHash.SHA256) : Cordapp {
|
||||
override val jarHash: SecureHash.SHA256,
|
||||
override val notaryService: Class<out NotaryService>? = null,
|
||||
/** Indicates whether the CorDapp is loaded from external sources, or generated on node startup (virtual). */
|
||||
val isLoaded: Boolean = true) : Cordapp {
|
||||
override val name: String = jarName(jarPath)
|
||||
|
||||
companion object {
|
||||
@ -37,16 +41,18 @@ data class CordappImpl(
|
||||
*
|
||||
* TODO: Also add [SchedulableFlow] as a Cordapp class
|
||||
*/
|
||||
override val cordappClasses: List<String> = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames
|
||||
override val cordappClasses: List<String> = run {
|
||||
val classList = rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass } + notaryService
|
||||
classList.mapNotNull { it?.name } + contractClassNames
|
||||
}
|
||||
|
||||
// TODO Why a seperate Info class and not just have the fields directly in CordappImpl?
|
||||
data class Info(val shortName: String, val vendor: String, val version: String, val minimumPlatformVersion: Int, val targetPlatformVersion: Int) {
|
||||
companion object {
|
||||
private const val UNKNOWN_VALUE = "Unknown"
|
||||
|
||||
const val UNKNOWN_VALUE = "Unknown"
|
||||
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE, 1, 1)
|
||||
}
|
||||
|
||||
fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
@ -18,6 +19,7 @@ import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.utilities.NonEmptySet
|
||||
import net.corda.core.utilities.toHexString
|
||||
import rx.Observable
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -125,6 +127,44 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
RELEVANT, NOT_RELEVANT, ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract constraint information associated with a [ContractState].
|
||||
* See [AttachmentConstraint]
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class ConstraintInfo(val constraint: AttachmentConstraint) {
|
||||
@CordaSerializable
|
||||
enum class Type {
|
||||
ALWAYS_ACCEPT, HASH, CZ_WHITELISTED, SIGNATURE
|
||||
}
|
||||
fun type(): Type {
|
||||
return when (constraint::class.java) {
|
||||
AlwaysAcceptAttachmentConstraint::class.java -> Type.ALWAYS_ACCEPT
|
||||
HashAttachmentConstraint::class.java -> Type.HASH
|
||||
WhitelistedByZoneAttachmentConstraint::class.java -> Type.CZ_WHITELISTED
|
||||
SignatureAttachmentConstraint::class.java -> Type.SIGNATURE
|
||||
else -> throw IllegalArgumentException("Invalid constraint type: $constraint")
|
||||
}
|
||||
}
|
||||
fun data(): ByteArray? {
|
||||
return when (type()) {
|
||||
Type.HASH -> (constraint as HashAttachmentConstraint).attachmentId.bytes
|
||||
Type.SIGNATURE -> (constraint as SignatureAttachmentConstraint).key.encoded
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
fun constraintInfo(type: Type, data: ByteArray?): ConstraintInfo {
|
||||
return when (type) {
|
||||
Type.ALWAYS_ACCEPT -> ConstraintInfo(AlwaysAcceptAttachmentConstraint)
|
||||
Type.HASH -> ConstraintInfo(HashAttachmentConstraint(SecureHash.parse(data!!.toHexString())))
|
||||
Type.CZ_WHITELISTED -> ConstraintInfo(WhitelistedByZoneAttachmentConstraint)
|
||||
Type.SIGNATURE -> ConstraintInfo(SignatureAttachmentConstraint(Crypto.decodePublicKey(data!!)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
enum class UpdateType {
|
||||
GENERAL, NOTARY_CHANGE, CONTRACT_UPGRADE
|
||||
@ -151,7 +191,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
val otherResults: List<Any>)
|
||||
|
||||
@CordaSerializable
|
||||
data class StateMetadata constructor(
|
||||
data class StateMetadata @JvmOverloads constructor(
|
||||
val ref: StateRef,
|
||||
val contractStateClassName: String,
|
||||
val recordedTime: Instant,
|
||||
@ -160,18 +200,9 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
val notary: AbstractParty?,
|
||||
val lockId: String?,
|
||||
val lockUpdateTime: Instant?,
|
||||
val relevancyStatus: Vault.RelevancyStatus?
|
||||
val relevancyStatus: Vault.RelevancyStatus? = null,
|
||||
val constraintInfo: ConstraintInfo? = null
|
||||
) {
|
||||
constructor(ref: StateRef,
|
||||
contractStateClassName: String,
|
||||
recordedTime: Instant,
|
||||
consumedTime: Instant?,
|
||||
status: Vault.StateStatus,
|
||||
notary: AbstractParty?,
|
||||
lockId: String?,
|
||||
lockUpdateTime: Instant?
|
||||
) : this(ref, contractStateClassName, recordedTime, consumedTime, status, notary, lockId, lockUpdateTime, null)
|
||||
|
||||
fun copy(
|
||||
ref: StateRef = this.ref,
|
||||
contractStateClassName: String = this.contractStateClassName,
|
||||
@ -184,6 +215,19 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
): StateMetadata {
|
||||
return StateMetadata(ref, contractStateClassName, recordedTime, consumedTime, status, notary, lockId, lockUpdateTime, null)
|
||||
}
|
||||
fun copy(
|
||||
ref: StateRef = this.ref,
|
||||
contractStateClassName: String = this.contractStateClassName,
|
||||
recordedTime: Instant = this.recordedTime,
|
||||
consumedTime: Instant? = this.consumedTime,
|
||||
status: Vault.StateStatus = this.status,
|
||||
notary: AbstractParty? = this.notary,
|
||||
lockId: String? = this.lockId,
|
||||
lockUpdateTime: Instant? = this.lockUpdateTime,
|
||||
relevancyStatus: Vault.RelevancyStatus?
|
||||
): StateMetadata {
|
||||
return StateMetadata(ref, contractStateClassName, recordedTime, consumedTime, status, notary, lockId, lockUpdateTime, relevancyStatus, ConstraintInfo(AlwaysAcceptAttachmentConstraint))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -194,6 +238,12 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum permissible size of contract constraint type data (for storage in vault states database table).
|
||||
* Maximum value equates to a CompositeKey with 10 EDDSA_ED25519_SHA512 keys stored in.
|
||||
*/
|
||||
const val MAX_CONSTRAINT_DATA_SIZE = 563
|
||||
|
||||
/**
|
||||
* A [VaultService] is responsible for securely and safely persisting the current state of a vault to storage. The
|
||||
* vault service vends immutable snapshots of the current vault for working with: if you build a transaction based
|
||||
@ -284,24 +334,26 @@ interface VaultService {
|
||||
|
||||
/**
|
||||
* Helper function to determine spendable states and soft locking them.
|
||||
* Currently performance will be worse than for the hand optimised version in `Cash.unconsumedCashStatesForSpending`.
|
||||
* However, this is fully generic and can operate with custom [FungibleAsset] states.
|
||||
* Currently performance will be worse than for the hand optimised version in
|
||||
* [Cash.unconsumedCashStatesForSpending]. However, this is fully generic and can operate with custom [FungibleState]
|
||||
* and [FungibleAsset] states.
|
||||
* @param lockId The [FlowLogic.runId]'s [UUID] of the current flow used to soft lock the states.
|
||||
* @param eligibleStatesQuery A custom query object that selects down to the appropriate subset of all states of the
|
||||
* [contractStateType]. e.g. by selecting on account, issuer, etc. The query is internally augmented with the
|
||||
* [StateStatus.UNCONSUMED], soft lock and contract type requirements.
|
||||
* @param amount The required amount of the asset, but with the issuer stripped off.
|
||||
* It is assumed that compatible issuer states will be filtered out by the [eligibleStatesQuery].
|
||||
* @param amount The required amount of the asset. It is assumed that compatible issuer states will be filtered out
|
||||
* by the [eligibleStatesQuery]. This method accepts both Amount<Issued<*>> and Amount<*>. Amount<Issued<*>> is
|
||||
* automatically unwrapped to Amount<*>.
|
||||
* @param contractStateType class type of the result set.
|
||||
* @return Returns a locked subset of the [eligibleStatesQuery] sufficient to satisfy the requested amount,
|
||||
* or else an empty list and no change in the stored lock states when their are insufficient resources available.
|
||||
*/
|
||||
@Suspendable
|
||||
@Throws(StatesNotAvailableException::class)
|
||||
fun <T : FungibleAsset<U>, U : Any> tryLockFungibleStatesForSpending(lockId: UUID,
|
||||
eligibleStatesQuery: QueryCriteria,
|
||||
amount: Amount<U>,
|
||||
contractStateType: Class<out T>): List<StateAndRef<T>>
|
||||
fun <T : FungibleState<*>> tryLockFungibleStatesForSpending(lockId: UUID,
|
||||
eligibleStatesQuery: QueryCriteria,
|
||||
amount: Amount<*>,
|
||||
contractStateType: Class<out T>): List<StateAndRef<T>>
|
||||
|
||||
// DOCSTART VaultQueryAPI
|
||||
/**
|
||||
|
@ -74,6 +74,8 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
|
||||
abstract class CommonQueryCriteria : QueryCriteria() {
|
||||
abstract val status: Vault.StateStatus
|
||||
open val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
|
||||
open val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet()
|
||||
open val constraints: Set<Vault.ConstraintInfo> = emptySet()
|
||||
abstract val contractStateTypes: Set<Class<out ContractState>>?
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseCriteria(this)
|
||||
@ -90,7 +92,9 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
|
||||
val notary: List<AbstractParty>? = null,
|
||||
val softLockingCondition: SoftLockingCondition? = null,
|
||||
val timeCondition: TimeCondition? = null,
|
||||
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
|
||||
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
|
||||
override val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
|
||||
override val constraints: Set<Vault.ConstraintInfo> = emptySet()
|
||||
) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
@ -164,6 +168,22 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FungibleStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultFungibleStates]
|
||||
*/
|
||||
data class FungibleStateQueryCriteria(
|
||||
val participants: List<AbstractParty>? = null,
|
||||
val quantity: ColumnPredicate<Long>? = null,
|
||||
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null,
|
||||
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
|
||||
) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
return parser.parseCriteria(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FungibleStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultFungibleStates]
|
||||
*/
|
||||
|
@ -184,7 +184,8 @@ data class Sort(val columns: Collection<SortColumn>) : BaseSort() {
|
||||
STATE_STATUS("stateStatus"),
|
||||
RECORDED_TIME("recordedTime"),
|
||||
CONSUMED_TIME("consumedTime"),
|
||||
LOCK_ID("lockId")
|
||||
LOCK_ID("lockId"),
|
||||
CONSTRAINT_TYPE("constraintType")
|
||||
}
|
||||
|
||||
enum class LinearStateAttribute(val attributeName: String) : Attribute {
|
||||
|
@ -19,4 +19,12 @@ import java.lang.annotation.Inherited
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Inherited
|
||||
annotation class CordaSerializable
|
||||
annotation class CordaSerializable
|
||||
|
||||
|
||||
/**
|
||||
* Used to annotate methods which expose calculated values that we want to be serialized for use by the class carpenter.
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.FUNCTION)
|
||||
annotation class SerializableCalculatedProperty
|
@ -13,75 +13,12 @@ import java.io.NotSerializableException
|
||||
object CheckpointSerializationDefaults {
|
||||
@DeleteForDJVM
|
||||
val CHECKPOINT_CONTEXT get() = effectiveSerializationEnv.checkpointContext
|
||||
val CHECKPOINT_SERIALIZATION_FACTORY get() = effectiveSerializationEnv.checkpointSerializationFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for serializing and deserializing objects at checkpoints, using Kryo serialization.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class CheckpointSerializationFactory(
|
||||
private val scheme: CheckpointSerializationScheme
|
||||
) {
|
||||
|
||||
val defaultContext: CheckpointSerializationContext get() = _currentContext.get() ?: effectiveSerializationEnv.checkpointContext
|
||||
|
||||
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
|
||||
|
||||
/**
|
||||
* Deserialize the bytes in to an object, using the prefixed bytes to determine the format.
|
||||
*
|
||||
* @param byteSequence The bytes to deserialize, including a format header prefix.
|
||||
* @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown.
|
||||
* @param context A context that configures various parameters to deserialization.
|
||||
*/
|
||||
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T {
|
||||
return withCurrentContext(context) { scheme.deserialize(byteSequence, clazz, context) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an object to bytes using the preferred serialization format version from the context.
|
||||
*
|
||||
* @param obj The object to be serialized.
|
||||
* @param context A context that configures various parameters to serialization, including the serialization format version.
|
||||
*/
|
||||
fun <T : Any> serialize(obj: T, context: CheckpointSerializationContext): SerializedBytes<T> {
|
||||
return withCurrentContext(context) { scheme.serialize(obj, context) }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${this.javaClass.name} scheme=$scheme ${creator.joinToString("\n")}"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CheckpointSerializationFactory && other.scheme == this.scheme
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = scheme.hashCode()
|
||||
|
||||
private val _currentContext = ThreadLocal<CheckpointSerializationContext?>()
|
||||
|
||||
/**
|
||||
* Change the current context inside the block to that supplied.
|
||||
*/
|
||||
fun <T> withCurrentContext(context: CheckpointSerializationContext?, block: () -> T): T {
|
||||
val priorContext = _currentContext.get()
|
||||
if (context != null) _currentContext.set(context)
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
if (context != null) _currentContext.set(priorContext)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val defaultFactory: CheckpointSerializationFactory get() = effectiveSerializationEnv.checkpointSerializationFactory
|
||||
}
|
||||
val CHECKPOINT_SERIALIZER get() = effectiveSerializationEnv.checkpointSerializer
|
||||
}
|
||||
|
||||
@KeepForDJVM
|
||||
@DoNotImplement
|
||||
interface CheckpointSerializationScheme {
|
||||
interface CheckpointSerializer {
|
||||
@Throws(NotSerializableException::class)
|
||||
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T
|
||||
|
||||
@ -167,32 +104,36 @@ interface CheckpointSerializationContext {
|
||||
/*
|
||||
* Convenience extension method for deserializing a ByteSequence, utilising the default factory.
|
||||
*/
|
||||
inline fun <reified T : Any> ByteSequence.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
|
||||
context: CheckpointSerializationContext): T {
|
||||
return serializationFactory.deserialize(this, T::class.java, context)
|
||||
@JvmOverloads
|
||||
inline fun <reified T : Any> ByteSequence.checkpointDeserialize(
|
||||
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
|
||||
return effectiveSerializationEnv.checkpointSerializer.deserialize(this, T::class.java, context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience extension method for deserializing SerializedBytes with type matching, utilising the default factory.
|
||||
*/
|
||||
inline fun <reified T : Any> SerializedBytes<T>.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
|
||||
context: CheckpointSerializationContext): T {
|
||||
return serializationFactory.deserialize(this, T::class.java, context)
|
||||
@JvmOverloads
|
||||
inline fun <reified T : Any> SerializedBytes<T>.checkpointDeserialize(
|
||||
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
|
||||
return effectiveSerializationEnv.checkpointSerializer.deserialize(this, T::class.java, context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience extension method for deserializing a ByteArray, utilising the default factory.
|
||||
*/
|
||||
inline fun <reified T : Any> ByteArray.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
|
||||
context: CheckpointSerializationContext): T {
|
||||
@JvmOverloads
|
||||
inline fun <reified T : Any> ByteArray.checkpointDeserialize(
|
||||
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
|
||||
require(isNotEmpty()) { "Empty bytes" }
|
||||
return this.sequence().checkpointDeserialize(serializationFactory, context)
|
||||
return this.sequence().checkpointDeserialize(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience extension method for serializing an object of type T, utilising the default factory.
|
||||
*/
|
||||
fun <T : Any> T.checkpointSerialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
|
||||
context: CheckpointSerializationContext): SerializedBytes<T> {
|
||||
return serializationFactory.serialize(this, context)
|
||||
@JvmOverloads
|
||||
fun <T : Any> T.checkpointSerialize(
|
||||
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): SerializedBytes<T> {
|
||||
return effectiveSerializationEnv.checkpointSerializer.serialize(this, context)
|
||||
}
|
@ -11,38 +11,63 @@ import net.corda.core.serialization.SerializationFactory
|
||||
|
||||
@KeepForDJVM
|
||||
interface SerializationEnvironment {
|
||||
|
||||
companion object {
|
||||
fun with(
|
||||
serializationFactory: SerializationFactory,
|
||||
p2pContext: SerializationContext,
|
||||
rpcServerContext: SerializationContext? = null,
|
||||
rpcClientContext: SerializationContext? = null,
|
||||
storageContext: SerializationContext? = null,
|
||||
|
||||
checkpointContext: CheckpointSerializationContext? = null,
|
||||
checkpointSerializer: CheckpointSerializer? = null
|
||||
): SerializationEnvironment =
|
||||
SerializationEnvironmentImpl(
|
||||
serializationFactory = serializationFactory,
|
||||
p2pContext = p2pContext,
|
||||
optionalRpcServerContext = rpcServerContext,
|
||||
optionalRpcClientContext = rpcClientContext,
|
||||
optionalStorageContext = storageContext,
|
||||
optionalCheckpointContext = checkpointContext,
|
||||
optionalCheckpointSerializer = checkpointSerializer
|
||||
)
|
||||
}
|
||||
|
||||
val serializationFactory: SerializationFactory
|
||||
val checkpointSerializationFactory: CheckpointSerializationFactory
|
||||
val p2pContext: SerializationContext
|
||||
val rpcServerContext: SerializationContext
|
||||
val rpcClientContext: SerializationContext
|
||||
val storageContext: SerializationContext
|
||||
|
||||
val checkpointSerializer: CheckpointSerializer
|
||||
val checkpointContext: CheckpointSerializationContext
|
||||
}
|
||||
|
||||
@KeepForDJVM
|
||||
open class SerializationEnvironmentImpl(
|
||||
private class SerializationEnvironmentImpl(
|
||||
override val serializationFactory: SerializationFactory,
|
||||
override val p2pContext: SerializationContext,
|
||||
rpcServerContext: SerializationContext? = null,
|
||||
rpcClientContext: SerializationContext? = null,
|
||||
storageContext: SerializationContext? = null,
|
||||
checkpointContext: CheckpointSerializationContext? = null,
|
||||
checkpointSerializationFactory: CheckpointSerializationFactory? = null) : SerializationEnvironment {
|
||||
// Those that are passed in as null are never inited:
|
||||
override lateinit var rpcServerContext: SerializationContext
|
||||
override lateinit var rpcClientContext: SerializationContext
|
||||
override lateinit var storageContext: SerializationContext
|
||||
override lateinit var checkpointContext: CheckpointSerializationContext
|
||||
override lateinit var checkpointSerializationFactory: CheckpointSerializationFactory
|
||||
private val optionalRpcServerContext: SerializationContext? = null,
|
||||
private val optionalRpcClientContext: SerializationContext? = null,
|
||||
private val optionalStorageContext: SerializationContext? = null,
|
||||
private val optionalCheckpointContext: CheckpointSerializationContext? = null,
|
||||
private val optionalCheckpointSerializer: CheckpointSerializer? = null) : SerializationEnvironment {
|
||||
|
||||
init {
|
||||
rpcServerContext?.let { this.rpcServerContext = it }
|
||||
rpcClientContext?.let { this.rpcClientContext = it }
|
||||
storageContext?.let { this.storageContext = it }
|
||||
checkpointContext?.let { this.checkpointContext = it }
|
||||
checkpointSerializationFactory?.let { this.checkpointSerializationFactory = it }
|
||||
}
|
||||
override val rpcServerContext: SerializationContext get() = optionalRpcServerContext ?:
|
||||
throw UnsupportedOperationException("RPC server serialization not supported in this environment")
|
||||
|
||||
override val rpcClientContext: SerializationContext get() = optionalRpcClientContext ?:
|
||||
throw UnsupportedOperationException("RPC client serialization not supported in this environment")
|
||||
|
||||
override val storageContext: SerializationContext get() = optionalStorageContext ?:
|
||||
throw UnsupportedOperationException("Storage serialization not supported in this environment")
|
||||
|
||||
override val checkpointContext: CheckpointSerializationContext get() = optionalCheckpointContext ?:
|
||||
throw UnsupportedOperationException("Checkpoint serialization not supported in this environment")
|
||||
|
||||
override val checkpointSerializer: CheckpointSerializer get() = optionalCheckpointSerializer ?:
|
||||
throw UnsupportedOperationException("Checkpoint serialization not supported in this environment")
|
||||
}
|
||||
|
||||
private val _nodeSerializationEnv = SimpleToggleField<SerializationEnvironment>("nodeSerializationEnv", true)
|
||||
|
@ -56,14 +56,24 @@ data class LedgerTransaction @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val logger = loggerFor<LedgerTransaction>()
|
||||
private fun contractClassFor(className: ContractClassName, classLoader: ClassLoader?): Try<Class<out Contract>> {
|
||||
return Try.on {
|
||||
(classLoader ?: this::class.java.classLoader)
|
||||
.loadClass(className)
|
||||
.asSubclass(Contract::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stateToContractClass(state: TransactionState<ContractState>): Try<Class<out Contract>> {
|
||||
return contractClassFor(state.contract, state.data::class.java.classLoader)
|
||||
}
|
||||
}
|
||||
|
||||
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
||||
val referenceStates: List<ContractState> get() = references.map { it.state.data }
|
||||
|
||||
private val inputAndOutputStates = inputs.asSequence().map { it.state } + outputs.asSequence()
|
||||
private val allStates = inputAndOutputStates + references.asSequence().map { it.state }
|
||||
private val inputAndOutputStates = inputs.map { it.state } + outputs
|
||||
private val allStates = inputAndOutputStates + references.map { it.state }
|
||||
|
||||
/**
|
||||
* Returns the typed input StateAndRef at the specified index
|
||||
@ -89,6 +99,30 @@ data class LedgerTransaction @JvmOverloads constructor(
|
||||
verifyContracts()
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that package ownership is respected.
|
||||
*
|
||||
* TODO - revisit once transaction contains network parameters.
|
||||
*/
|
||||
private fun validatePackageOwnership(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>) {
|
||||
// This should never happen once we have network parameters in the transaction.
|
||||
if (networkParameters == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val contractsAndOwners = allStates.mapNotNull { transactionState ->
|
||||
val contractClassName = transactionState.contract
|
||||
networkParameters.getOwnerOf(contractClassName)?.let { contractClassName to it }
|
||||
}.toMap()
|
||||
|
||||
contractsAndOwners.forEach { contract, owner ->
|
||||
val attachment = contractAttachmentsByContract[contract]!!
|
||||
if (!owner.isFulfilledBy(attachment.signers)) {
|
||||
throw TransactionVerificationException.ContractAttachmentNotSignedByPackageOwnerException(this.id, id, contract)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all input and output [TransactionState]s, validates that the wrapped [ContractState] matches up with the
|
||||
* wrapped [Contract], as declared by the [BelongsToContract] annotation on the [ContractState]'s class.
|
||||
@ -261,30 +295,79 @@ data class LedgerTransaction @JvmOverloads constructor(
|
||||
|
||||
private fun checkEncumbrancesValid() {
|
||||
// Validate that all encumbrances exist within the set of input states.
|
||||
val encumberedInputs = inputs.filter { it.state.encumbrance != null }
|
||||
encumberedInputs.forEach { (state, ref) ->
|
||||
val encumbranceStateExists = inputs.any {
|
||||
it.ref.txhash == ref.txhash && it.ref.index == state.encumbrance
|
||||
}
|
||||
if (!encumbranceStateExists) {
|
||||
inputs.filter { it.state.encumbrance != null }
|
||||
.forEach { (state, ref) -> checkInputEncumbranceStateExists(state, ref) }
|
||||
|
||||
// Check that in the outputs,
|
||||
// a) an encumbered state does not refer to itself as the encumbrance
|
||||
// b) the number of outputs can contain the encumbrance
|
||||
// c) the bi-directionality (full cycle) property is satisfied.
|
||||
val statesAndEncumbrance = outputs.withIndex().filter { it.value.encumbrance != null }.map { Pair(it.index, it.value.encumbrance!!) }
|
||||
if (!statesAndEncumbrance.isEmpty()) {
|
||||
checkOutputEncumbrances(statesAndEncumbrance)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInputEncumbranceStateExists(state: TransactionState<ContractState>, ref: StateRef) {
|
||||
val encumbranceStateExists = inputs.any {
|
||||
it.ref.txhash == ref.txhash && it.ref.index == state.encumbrance
|
||||
}
|
||||
if (!encumbranceStateExists) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
id,
|
||||
state.encumbrance!!,
|
||||
TransactionVerificationException.Direction.INPUT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Using basic graph theory, a full cycle of encumbered (co-dependent) states should exist to achieve bi-directional
|
||||
// encumbrances. This property is important to ensure that no states involved in an encumbrance-relationship
|
||||
// can be spent on their own. Briefly, if any of the states is having more than one encumbrance references by
|
||||
// other states, a full cycle detection will fail. As a result, all of the encumbered states must be present
|
||||
// as "from" and "to" only once (or zero times if no encumbrance takes place). For instance,
|
||||
// a -> b
|
||||
// c -> b and a -> b
|
||||
// b -> a b -> c
|
||||
// do not satisfy the bi-directionality (full cycle) property.
|
||||
//
|
||||
// In the first example "b" appears twice in encumbrance ("to") list and "c" exists in the encumbered ("from") list only.
|
||||
// Due the above, one could consume "a" and "b" in the same transaction and then, because "b" is already consumed, "c" cannot be spent.
|
||||
//
|
||||
// Similarly, the second example does not form a full cycle because "a" and "c" exist in one of the lists only.
|
||||
// As a result, one can consume "b" and "c" in the same transactions, which will make "a" impossible to be spent.
|
||||
//
|
||||
// On other hand the following are valid constructions:
|
||||
// a -> b a -> c
|
||||
// b -> c and c -> b
|
||||
// c -> a b -> a
|
||||
// and form a full cycle, meaning that the bi-directionality property is satisfied.
|
||||
private fun checkOutputEncumbrances(statesAndEncumbrance: List<Pair<Int, Int>>) {
|
||||
// [Set] of "from" (encumbered states).
|
||||
val encumberedSet = mutableSetOf<Int>()
|
||||
// [Set] of "to" (encumbrance states).
|
||||
val encumbranceSet = mutableSetOf<Int>()
|
||||
// Update both [Set]s.
|
||||
statesAndEncumbrance.forEach { (statePosition, encumbrance) ->
|
||||
// Check it does not refer to itself.
|
||||
if (statePosition == encumbrance || encumbrance >= outputs.size) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
id,
|
||||
state.encumbrance!!,
|
||||
TransactionVerificationException.Direction.INPUT
|
||||
)
|
||||
encumbrance,
|
||||
TransactionVerificationException.Direction.OUTPUT)
|
||||
} else {
|
||||
encumberedSet.add(statePosition) // Guaranteed to have unique elements.
|
||||
if (!encumbranceSet.add(encumbrance)) {
|
||||
throw TransactionVerificationException.TransactionDuplicateEncumbranceException(id, encumbrance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that, in the outputs, an encumbered state does not refer to itself as the encumbrance,
|
||||
// and that the number of outputs can contain the encumbrance.
|
||||
for ((i, output) in outputs.withIndex()) {
|
||||
val encumbranceIndex = output.encumbrance ?: continue
|
||||
if (encumbranceIndex == i || encumbranceIndex >= outputs.size) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
id,
|
||||
encumbranceIndex,
|
||||
TransactionVerificationException.Direction.OUTPUT)
|
||||
}
|
||||
// At this stage we have ensured that "from" and "to" [Set]s are equal in size, but we should check their
|
||||
// elements do indeed match. If they don't match, we return their symmetric difference (disjunctive union).
|
||||
val symmetricDifference = (encumberedSet union encumbranceSet).subtract(encumberedSet intersect encumbranceSet)
|
||||
if (symmetricDifference.isNotEmpty()) {
|
||||
// At least one encumbered state is not in the [encumbranceSet] and vice versa.
|
||||
throw TransactionVerificationException.TransactionNonMatchingEncumbranceException(id, symmetricDifference)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,13 +102,21 @@ data class NotaryChangeLedgerTransaction(
|
||||
|
||||
override val references: List<StateAndRef<ContractState>> = emptyList()
|
||||
|
||||
/** We compute the outputs on demand by applying the notary field modification to the inputs */
|
||||
/** We compute the outputs on demand by applying the notary field modification to the inputs. */
|
||||
override val outputs: List<TransactionState<ContractState>>
|
||||
get() = inputs.mapIndexed { pos, (state) ->
|
||||
get() = computeOutputs()
|
||||
|
||||
private fun computeOutputs(): List<TransactionState<ContractState>> {
|
||||
val inputPositionIndex: Map<StateRef, Int> = inputs.mapIndexed { index, stateAndRef -> stateAndRef.ref to index }.toMap()
|
||||
return inputs.map { (state, ref) ->
|
||||
if (state.encumbrance != null) {
|
||||
state.copy(notary = newNotary, encumbrance = pos + 1)
|
||||
val encumbranceStateRef = StateRef(ref.txhash, state.encumbrance)
|
||||
val encumbrancePosition = inputPositionIndex[encumbranceStateRef]
|
||||
?: throw IllegalStateException("Unable to generate output states – transaction not constructed correctly.")
|
||||
state.copy(notary = newNotary, encumbrance = encumbrancePosition)
|
||||
} else state.copy(notary = newNotary)
|
||||
}
|
||||
}
|
||||
|
||||
override val requiredSigningKeys: Set<PublicKey>
|
||||
get() = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet() + notary.owningKey
|
||||
@ -118,18 +126,16 @@ data class NotaryChangeLedgerTransaction(
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that encumbrances have been included in the inputs. The [NotaryChangeFlow] guarantees that an encumbrance
|
||||
* will follow its encumbered state in the inputs.
|
||||
* Check that encumbrances have been included in the inputs.
|
||||
*/
|
||||
private fun checkEncumbrances() {
|
||||
inputs.forEachIndexed { i, (state, ref) ->
|
||||
state.encumbrance?.let {
|
||||
val nextIndex = i + 1
|
||||
fun nextStateIsEncumbrance() = (inputs[nextIndex].ref.txhash == ref.txhash) && (inputs[nextIndex].ref.index == it)
|
||||
if (nextIndex >= inputs.size || !nextStateIsEncumbrance()) {
|
||||
val encumberedStates = inputs.asSequence().filter { it.state.encumbrance != null }.associateBy { it.ref }
|
||||
if (encumberedStates.isNotEmpty()) {
|
||||
inputs.forEach { (state, ref) ->
|
||||
if (StateRef(ref.txhash, state.encumbrance!!) !in encumberedStates) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
id,
|
||||
it,
|
||||
state.encumbrance,
|
||||
TransactionVerificationException.Direction.INPUT)
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
return descriptions
|
||||
}
|
||||
|
||||
@DeleteForDJVM
|
||||
@VisibleForTesting
|
||||
fun withAdditionalSignature(keyPair: KeyPair, signatureMetadata: SignatureMetadata): SignedTransaction {
|
||||
val signableData = SignableData(tx.id, signatureMetadata)
|
||||
|
@ -29,7 +29,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
|
||||
*/
|
||||
abstract val bytes: ByteArray
|
||||
|
||||
/** Returns a [ByteArrayInputStream] of the bytes */
|
||||
/** Returns a [ByteArrayInputStream] of the bytes. */
|
||||
fun open() = ByteArrayInputStream(_bytes, offset, size)
|
||||
|
||||
/**
|
||||
@ -41,8 +41,6 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun subSequence(offset: Int, size: Int): ByteSequence {
|
||||
require(offset >= 0)
|
||||
require(offset + size <= this.size)
|
||||
// Intentionally use bytes rather than _bytes, to mirror the copy-or-not behaviour of that property.
|
||||
return if (offset == 0 && size == this.size) this else of(bytes, this.offset + offset, size)
|
||||
}
|
||||
@ -108,7 +106,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
|
||||
return Integer.signum(unsignedThis - unsignedOther)
|
||||
}
|
||||
}
|
||||
// First min bytes is the same, so now resort to size
|
||||
// First min bytes is the same, so now resort to size.
|
||||
return Integer.signum(this.size - other.size)
|
||||
}
|
||||
|
||||
@ -190,12 +188,12 @@ fun ByteArray.toHexString(): String = DatatypeConverter.printHexBinary(this)
|
||||
fun String.parseAsHex(): ByteArray = DatatypeConverter.parseHexBinary(this)
|
||||
|
||||
/**
|
||||
* Class is public for serialization purposes
|
||||
* Class is public for serialization purposes.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class OpaqueBytesSubSequence(override val bytes: ByteArray, offset: Int, size: Int) : ByteSequence(bytes, offset, size) {
|
||||
init {
|
||||
require(offset >= 0 && offset < bytes.size)
|
||||
require(size >= 0 && size <= bytes.size)
|
||||
require(size >= 0 && offset + size <= bytes.size)
|
||||
}
|
||||
}
|
||||
|
@ -30,10 +30,11 @@ import java.util.*
|
||||
* using the [Observable] subscribeOn call.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class ProgressTracker(vararg steps: Step) {
|
||||
class ProgressTracker(vararg inputSteps: Step) {
|
||||
|
||||
@CordaSerializable
|
||||
sealed class Change(val progressTracker: ProgressTracker) {
|
||||
data class Position(val tracker: ProgressTracker, val newStep: Step) : Change(tracker) {
|
||||
data class Position(val tracker: ProgressTracker, val newStep: Step) : Change(tracker) {
|
||||
override fun toString() = newStep.label
|
||||
}
|
||||
|
||||
@ -64,6 +65,10 @@ class ProgressTracker(vararg steps: Step) {
|
||||
override fun equals(other: Any?) = other === UNSTARTED
|
||||
}
|
||||
|
||||
object STARTING : Step("Starting") {
|
||||
override fun equals(other: Any?) = other === STARTING
|
||||
}
|
||||
|
||||
object DONE : Step("Done") {
|
||||
override fun equals(other: Any?) = other === DONE
|
||||
}
|
||||
@ -74,7 +79,7 @@ class ProgressTracker(vararg steps: Step) {
|
||||
private val childProgressTrackers = mutableMapOf<Step, Child>()
|
||||
|
||||
/** The steps in this tracker, same as the steps passed to the constructor but with UNSTARTED and DONE inserted. */
|
||||
val steps = arrayOf(UNSTARTED, *steps, DONE)
|
||||
val steps = arrayOf(UNSTARTED, STARTING, *inputSteps, DONE)
|
||||
|
||||
private var _allStepsCache: List<Pair<Int, Step>> = _allSteps()
|
||||
|
||||
@ -83,42 +88,16 @@ class ProgressTracker(vararg steps: Step) {
|
||||
private val _stepsTreeChanges by transient { PublishSubject.create<List<Pair<Int, String>>>() }
|
||||
private val _stepsTreeIndexChanges by transient { PublishSubject.create<Int>() }
|
||||
|
||||
|
||||
|
||||
init {
|
||||
steps.forEach {
|
||||
val childTracker = it.childProgressTracker()
|
||||
if (childTracker != null) {
|
||||
setChildProgressTracker(it, childTracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE) */
|
||||
var stepIndex: Int = 0
|
||||
private set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
/** The zero-bases index of the current step in a [allStepsLabels] list */
|
||||
var stepsTreeIndex: Int = -1
|
||||
private set(value) {
|
||||
field = value
|
||||
_stepsTreeIndexChanges.onNext(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading returns the value of steps[stepIndex], writing moves the position of the current tracker. Once moved to
|
||||
* the [DONE] state, this tracker is finished and the current step cannot be moved again.
|
||||
*/
|
||||
var currentStep: Step
|
||||
get() = steps[stepIndex]
|
||||
set(value) {
|
||||
check(!hasEnded) { "Cannot rewind a progress tracker once it has ended" }
|
||||
check((value === DONE && hasEnded) || !hasEnded) {
|
||||
"Cannot rewind a progress tracker once it has ended"
|
||||
}
|
||||
if (currentStep == value) return
|
||||
|
||||
val index = steps.indexOf(value)
|
||||
require(index != -1, { "Step ${value.label} not found in progress tracker." })
|
||||
require(index != -1) { "Step ${value.label} not found in progress tracker." }
|
||||
|
||||
if (index < stepIndex) {
|
||||
// We are going backwards: unlink and unsubscribe from any child nodes that we're rolling back
|
||||
@ -144,6 +123,39 @@ class ProgressTracker(vararg steps: Step) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
steps.forEach {
|
||||
configureChildTrackerForStep(it)
|
||||
}
|
||||
this.currentStep = UNSTARTED
|
||||
}
|
||||
|
||||
private fun configureChildTrackerForStep(it: Step) {
|
||||
val childTracker = it.childProgressTracker()
|
||||
if (childTracker != null) {
|
||||
setChildProgressTracker(it, childTracker)
|
||||
}
|
||||
}
|
||||
|
||||
/** The zero-based index of the current step in the [steps] array (i.e. with UNSTARTED and DONE) */
|
||||
var stepIndex: Int = 0
|
||||
private set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
/** The zero-bases index of the current step in a [allStepsLabels] list */
|
||||
var stepsTreeIndex: Int = -1
|
||||
private set(value) {
|
||||
field = value
|
||||
_stepsTreeIndexChanges.onNext(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading returns the value of steps[stepIndex], writing moves the position of the current tracker. Once moved to
|
||||
* the [DONE] state, this tracker is finished and the current step cannot be moved again.
|
||||
*/
|
||||
|
||||
/** Returns the current step, descending into children to find the deepest step we are up to. */
|
||||
val currentStepRecursive: Step
|
||||
get() = getChildProgressTracker(currentStep)?.currentStepRecursive ?: currentStep
|
||||
@ -263,7 +275,7 @@ class ProgressTracker(vararg steps: Step) {
|
||||
/**
|
||||
* An observable stream of changes to the [allStepsLabels]
|
||||
*/
|
||||
val stepsTreeChanges: Observable<List<Pair<Int,String>>> get() = _stepsTreeChanges
|
||||
val stepsTreeChanges: Observable<List<Pair<Int, String>>> get() = _stepsTreeChanges
|
||||
|
||||
/**
|
||||
* An observable stream of changes to the [stepsTreeIndex]
|
||||
@ -272,6 +284,10 @@ class ProgressTracker(vararg steps: Step) {
|
||||
|
||||
/** Returns true if the progress tracker has ended, either by reaching the [DONE] step or prematurely with an error */
|
||||
val hasEnded: Boolean get() = _changes.hasCompleted() || _changes.hasThrowable()
|
||||
|
||||
companion object {
|
||||
val DEFAULT_TRACKER = { ProgressTracker() }
|
||||
}
|
||||
}
|
||||
// TODO: Expose the concept of errors.
|
||||
// TODO: It'd be helpful if this class was at least partly thread safe.
|
||||
|
@ -10,11 +10,9 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.smoketesting.NodeConfig
|
||||
import net.corda.smoketesting.NodeProcess
|
||||
import net.corda.testing.common.internal.ProjectStructure
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.streams.toList
|
||||
@ -23,12 +21,6 @@ class NodeVersioningTest {
|
||||
private companion object {
|
||||
val user = User("user1", "test", permissions = setOf("ALL"))
|
||||
val port = AtomicInteger(15100)
|
||||
|
||||
val expectedPlatformVersion = (ProjectStructure.projectRootDir / "constants.properties").read {
|
||||
val constants = Properties()
|
||||
constants.load(it)
|
||||
constants.getProperty("platformVersion").toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private val factory = NodeProcess.Factory()
|
||||
@ -45,7 +37,7 @@ class NodeVersioningTest {
|
||||
@Test
|
||||
fun `platform version in manifest file`() {
|
||||
val manifest = JarFile(factory.cordaJar.toFile()).manifest
|
||||
assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(expectedPlatformVersion)
|
||||
assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(PLATFORM_VERSION)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -60,9 +52,9 @@ class NodeVersioningTest {
|
||||
factory.create(aliceConfig).use { alice ->
|
||||
alice.connect().use {
|
||||
val rpc = it.proxy
|
||||
assertThat(rpc.protocolVersion).isEqualTo(expectedPlatformVersion)
|
||||
assertThat(rpc.nodeInfo().platformVersion).isEqualTo(expectedPlatformVersion)
|
||||
assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(expectedPlatformVersion)
|
||||
assertThat(rpc.protocolVersion).isEqualTo(PLATFORM_VERSION)
|
||||
assertThat(rpc.nodeInfo().platformVersion).isEqualTo(PLATFORM_VERSION)
|
||||
assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(PLATFORM_VERSION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.corda.core.flows;
|
||||
|
||||
import net.corda.core.serialization.internal.CheckpointSerializationDefaults;
|
||||
import net.corda.core.serialization.internal.CheckpointSerializationFactory;
|
||||
import net.corda.core.serialization.SerializationDefaults;
|
||||
import net.corda.core.serialization.SerializationFactory;
|
||||
import net.corda.testing.core.SerializationEnvironmentRule;
|
||||
@ -32,12 +30,10 @@ public class SerializationApiInJavaTest {
|
||||
SerializationDefaults defaults = SerializationDefaults.INSTANCE;
|
||||
SerializationFactory factory = defaults.getSERIALIZATION_FACTORY();
|
||||
|
||||
CheckpointSerializationDefaults checkpointDefaults = CheckpointSerializationDefaults.INSTANCE;
|
||||
CheckpointSerializationFactory checkpointSerializationFactory = checkpointDefaults.getCHECKPOINT_SERIALIZATION_FACTORY();
|
||||
serialize("hello", factory, defaults.getP2P_CONTEXT());
|
||||
serialize("hello", factory, defaults.getRPC_SERVER_CONTEXT());
|
||||
serialize("hello", factory, defaults.getRPC_CLIENT_CONTEXT());
|
||||
serialize("hello", factory, defaults.getSTORAGE_CONTEXT());
|
||||
checkpointSerialize("hello", checkpointSerializationFactory, checkpointDefaults.getCHECKPOINT_CONTEXT());
|
||||
checkpointSerialize("hello");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
package net.corda.core.crypto
|
||||
|
||||
import com.google.common.collect.Sets
|
||||
import net.corda.core.crypto.Crypto.ECDSA_SECP256K1_SHA256
|
||||
import net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256
|
||||
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
|
||||
import net.corda.core.crypto.Crypto.RSA_SHA256
|
||||
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.i2p.crypto.eddsa.EdDSAKey
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||
@ -30,17 +36,20 @@ import kotlin.test.*
|
||||
*/
|
||||
class CryptoUtilsTest {
|
||||
|
||||
private val testBytes = "Hello World".toByteArray()
|
||||
companion object {
|
||||
private val testBytes = "Hello World".toByteArray()
|
||||
private val test100ZeroBytes = ByteArray(100)
|
||||
}
|
||||
|
||||
// key generation test
|
||||
@Test
|
||||
fun `Generate key pairs`() {
|
||||
// testing supported algorithms
|
||||
val rsaKeyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val ecdsaKKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val ecdsaRKeyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val eddsaKeyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val sphincsKeyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val rsaKeyPair = Crypto.generateKeyPair(RSA_SHA256)
|
||||
val ecdsaKKeyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val ecdsaRKeyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val eddsaKeyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val sphincsKeyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
|
||||
|
||||
// not null private keys
|
||||
assertNotNull(rsaKeyPair.private)
|
||||
@ -69,7 +78,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `RSA full process keygen-sign-verify`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(RSA_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
// test for some data
|
||||
val signedData = Crypto.doSign(privKey, testBytes)
|
||||
@ -101,8 +110,8 @@ class CryptoUtilsTest {
|
||||
}
|
||||
|
||||
// test for zero bytes data
|
||||
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
|
||||
val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
|
||||
assertTrue(verificationZeros)
|
||||
|
||||
// test for 1MB of data (I successfully tested it locally for 1GB as well)
|
||||
@ -124,7 +133,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256k1 full process keygen-sign-verify`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
// test for some data
|
||||
val signedData = Crypto.doSign(privKey, testBytes)
|
||||
@ -156,8 +165,8 @@ class CryptoUtilsTest {
|
||||
}
|
||||
|
||||
// test for zero bytes data
|
||||
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
|
||||
val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
|
||||
assertTrue(verificationZeros)
|
||||
|
||||
// test for 1MB of data (I successfully tested it locally for 1GB as well)
|
||||
@ -179,7 +188,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256r1 full process keygen-sign-verify`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
// test for some data
|
||||
val signedData = Crypto.doSign(privKey, testBytes)
|
||||
@ -211,8 +220,8 @@ class CryptoUtilsTest {
|
||||
}
|
||||
|
||||
// test for zero bytes data
|
||||
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
|
||||
val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
|
||||
assertTrue(verificationZeros)
|
||||
|
||||
// test for 1MB of data (I successfully tested it locally for 1GB as well)
|
||||
@ -234,7 +243,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `EDDSA ed25519 full process keygen-sign-verify`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val (privKey, pubKey) = keyPair
|
||||
// test for some data
|
||||
val signedData = Crypto.doSign(privKey, testBytes)
|
||||
@ -266,8 +275,8 @@ class CryptoUtilsTest {
|
||||
}
|
||||
|
||||
// test for zero bytes data
|
||||
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
|
||||
val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
|
||||
assertTrue(verificationZeros)
|
||||
|
||||
// test for 1MB of data (I successfully tested it locally for 1GB as well)
|
||||
@ -289,7 +298,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `SPHINCS-256 full process keygen-sign-verify`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
// test for some data
|
||||
val signedData = Crypto.doSign(privKey, testBytes)
|
||||
@ -321,8 +330,8 @@ class CryptoUtilsTest {
|
||||
}
|
||||
|
||||
// test for zero bytes data
|
||||
val signedDataZeros = Crypto.doSign(privKey, ByteArray(100))
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, ByteArray(100))
|
||||
val signedDataZeros = Crypto.doSign(privKey, test100ZeroBytes)
|
||||
val verificationZeros = Crypto.doVerify(pubKey, signedDataZeros, test100ZeroBytes)
|
||||
assertTrue(verificationZeros)
|
||||
|
||||
// test for 1MB of data (I successfully tested it locally for 1GB as well)
|
||||
@ -354,7 +363,7 @@ class CryptoUtilsTest {
|
||||
@Test
|
||||
fun `RSA encode decode keys - required for serialization`() {
|
||||
// Generate key pair.
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(RSA_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
|
||||
// Encode and decode private key.
|
||||
@ -369,7 +378,7 @@ class CryptoUtilsTest {
|
||||
@Test
|
||||
fun `ECDSA secp256k1 encode decode keys - required for serialization`() {
|
||||
// Generate key pair.
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
|
||||
// Encode and decode private key.
|
||||
@ -384,7 +393,7 @@ class CryptoUtilsTest {
|
||||
@Test
|
||||
fun `ECDSA secp256r1 encode decode keys - required for serialization`() {
|
||||
// Generate key pair.
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
|
||||
// Encode and decode private key.
|
||||
@ -399,7 +408,7 @@ class CryptoUtilsTest {
|
||||
@Test
|
||||
fun `EdDSA encode decode keys - required for serialization`() {
|
||||
// Generate key pair.
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPair = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val (privKey, pubKey) = keyPair
|
||||
|
||||
// Encode and decode private key.
|
||||
@ -414,7 +423,7 @@ class CryptoUtilsTest {
|
||||
@Test
|
||||
fun `SPHINCS-256 encode decode keys - required for serialization`() {
|
||||
// Generate key pair.
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(SPHINCS256_SHA256)
|
||||
val privKey: BCSphincs256PrivateKey = keyPair.private as BCSphincs256PrivateKey
|
||||
val pubKey: BCSphincs256PublicKey = keyPair.public as BCSphincs256PublicKey
|
||||
|
||||
@ -443,7 +452,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `RSA scheme finder by key type`() {
|
||||
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPairRSA = Crypto.generateKeyPair(RSA_SHA256)
|
||||
val (privRSA, pubRSA) = keyPairRSA
|
||||
assertEquals(privRSA.algorithm, "RSA")
|
||||
assertEquals(pubRSA.algorithm, "RSA")
|
||||
@ -451,7 +460,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256k1 scheme finder by key type`() {
|
||||
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPair = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val (privKey, pubKey) = keyPair
|
||||
|
||||
// Encode and decode private key.
|
||||
@ -466,7 +475,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256r1 scheme finder by key type`() {
|
||||
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (privR1, pubR1) = keyPairR1
|
||||
assertEquals(privR1.algorithm, "ECDSA")
|
||||
assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
|
||||
@ -476,7 +485,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `EdDSA scheme finder by key type`() {
|
||||
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPairEd = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val (privEd, pubEd) = keyPairEd
|
||||
|
||||
assertEquals(privEd.algorithm, "EdDSA")
|
||||
@ -487,7 +496,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `SPHINCS-256 scheme finder by key type`() {
|
||||
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPairSP = Crypto.generateKeyPair(SPHINCS256_SHA256)
|
||||
val (privSP, pubSP) = keyPairSP
|
||||
assertEquals(privSP.algorithm, "SPHINCS-256")
|
||||
assertEquals(pubSP.algorithm, "SPHINCS-256")
|
||||
@ -495,7 +504,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Automatic EdDSA key-type detection and decoding`() {
|
||||
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPairEd = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val (privEd, pubEd) = keyPairEd
|
||||
val encodedPrivEd = privEd.encoded
|
||||
val encodedPubEd = pubEd.encoded
|
||||
@ -511,7 +520,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Automatic ECDSA secp256k1 key-type detection and decoding`() {
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val (privK1, pubK1) = keyPairK1
|
||||
val encodedPrivK1 = privK1.encoded
|
||||
val encodedPubK1 = pubK1.encoded
|
||||
@ -527,7 +536,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Automatic ECDSA secp256r1 key-type detection and decoding`() {
|
||||
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (privR1, pubR1) = keyPairR1
|
||||
val encodedPrivR1 = privR1.encoded
|
||||
val encodedPubR1 = pubR1.encoded
|
||||
@ -543,7 +552,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Automatic RSA key-type detection and decoding`() {
|
||||
val keyPairRSA = Crypto.generateKeyPair(Crypto.RSA_SHA256)
|
||||
val keyPairRSA = Crypto.generateKeyPair(RSA_SHA256)
|
||||
val (privRSA, pubRSA) = keyPairRSA
|
||||
val encodedPrivRSA = privRSA.encoded
|
||||
val encodedPubRSA = pubRSA.encoded
|
||||
@ -559,7 +568,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Automatic SPHINCS-256 key-type detection and decoding`() {
|
||||
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
|
||||
val keyPairSP = Crypto.generateKeyPair(SPHINCS256_SHA256)
|
||||
val (privSP, pubSP) = keyPairSP
|
||||
val encodedPrivSP = privSP.encoded
|
||||
val encodedPubSP = pubSP.encoded
|
||||
@ -575,12 +584,12 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Failure test between K1 and R1 keys`() {
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val privK1 = keyPairK1.private
|
||||
val encodedPrivK1 = privK1.encoded
|
||||
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
|
||||
|
||||
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val privR1 = keyPairR1.private
|
||||
val encodedPrivR1 = privR1.encoded
|
||||
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
|
||||
@ -590,7 +599,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Decoding Failure on randomdata as key`() {
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val privK1 = keyPairK1.private
|
||||
val encodedPrivK1 = privK1.encoded
|
||||
|
||||
@ -610,7 +619,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Decoding Failure on malformed keys`() {
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val privK1 = keyPairK1.private
|
||||
val encodedPrivK1 = privK1.encoded
|
||||
|
||||
@ -630,25 +639,25 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `Check ECDSA public key on curve`() {
|
||||
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val keyPairK1 = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val pubK1 = keyPairK1.public as BCECPublicKey
|
||||
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256K1_SHA256, pubK1))
|
||||
assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256K1_SHA256, pubK1))
|
||||
// use R1 curve for check.
|
||||
assertFalse(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubK1))
|
||||
assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubK1))
|
||||
// use ed25519 curve for check.
|
||||
assertFalse(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, pubK1))
|
||||
assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubK1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Check EdDSA public key on curve`() {
|
||||
val keyPairEdDSA = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val pubEdDSA = keyPairEdDSA.public
|
||||
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, pubEdDSA))
|
||||
assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA))
|
||||
// Use R1 curve for check.
|
||||
assertFalse(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubEdDSA))
|
||||
assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA))
|
||||
// Check for point at infinity.
|
||||
val pubKeySpec = EdDSAPublicKeySpec((Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec)
|
||||
assertFalse(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec)))
|
||||
val pubKeySpec = EdDSAPublicKeySpec((EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec)
|
||||
assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec)))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
@ -658,12 +667,12 @@ class CryptoUtilsTest {
|
||||
val pairSun = keyGen.generateKeyPair()
|
||||
val pubSun = pairSun.public
|
||||
// Should fail as pubSun is not a BCECPublicKey.
|
||||
Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, pubSun)
|
||||
Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubSun)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256R1 deterministic key generation`() {
|
||||
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
|
||||
val (priv, pub) = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
|
||||
|
||||
// Check scheme.
|
||||
@ -673,11 +682,11 @@ class CryptoUtilsTest {
|
||||
assertTrue(dpub is BCECPublicKey)
|
||||
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
|
||||
assertEquals((dpub as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.ECDSA_SECP256R1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256R1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), ECDSA_SECP256R1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), ECDSA_SECP256R1_SHA256)
|
||||
|
||||
// Validate public key.
|
||||
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256R1_SHA256, dpub))
|
||||
assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, dpub))
|
||||
|
||||
// Try to sign/verify.
|
||||
val signedData = Crypto.doSign(dpriv, testBytes)
|
||||
@ -704,7 +713,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `ECDSA secp256K1 deterministic key generation`() {
|
||||
val (priv, pub) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
|
||||
val (priv, pub) = Crypto.generateKeyPair(ECDSA_SECP256K1_SHA256)
|
||||
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
|
||||
|
||||
// Check scheme.
|
||||
@ -714,11 +723,11 @@ class CryptoUtilsTest {
|
||||
assertTrue(dpub is BCECPublicKey)
|
||||
assertEquals((dpriv as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
|
||||
assertEquals((dpub as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.ECDSA_SECP256K1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.ECDSA_SECP256K1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), ECDSA_SECP256K1_SHA256)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), ECDSA_SECP256K1_SHA256)
|
||||
|
||||
// Validate public key.
|
||||
assertTrue(Crypto.publicKeyOnCurve(Crypto.ECDSA_SECP256K1_SHA256, dpub))
|
||||
assertTrue(Crypto.publicKeyOnCurve(ECDSA_SECP256K1_SHA256, dpub))
|
||||
|
||||
// Try to sign/verify.
|
||||
val signedData = Crypto.doSign(dpriv, testBytes)
|
||||
@ -745,7 +754,7 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `EdDSA ed25519 deterministic key generation`() {
|
||||
val (priv, pub) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
|
||||
val (priv, pub) = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
|
||||
val (dpriv, dpub) = Crypto.deriveKeyPair(priv, "seed-1".toByteArray())
|
||||
|
||||
// Check scheme.
|
||||
@ -755,11 +764,11 @@ class CryptoUtilsTest {
|
||||
assertTrue(dpub is EdDSAPublicKey)
|
||||
assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
|
||||
assertEquals((dpub as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), Crypto.EDDSA_ED25519_SHA512)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), Crypto.EDDSA_ED25519_SHA512)
|
||||
assertEquals(Crypto.findSignatureScheme(dpriv), EDDSA_ED25519_SHA512)
|
||||
assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512)
|
||||
|
||||
// Validate public key.
|
||||
assertTrue(Crypto.publicKeyOnCurve(Crypto.EDDSA_ED25519_SHA512, dpub))
|
||||
assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, dpub))
|
||||
|
||||
// Try to sign/verify.
|
||||
val signedData = Crypto.doSign(dpriv, testBytes)
|
||||
@ -786,110 +795,131 @@ class CryptoUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `EdDSA ed25519 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("10"))
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("10"))
|
||||
assertEquals("DLBL3iHCp9uRReWhhCGfCsrxZZpfAm9h9GLbfN8ijqXTq", keyPairPositive.public.toStringShort())
|
||||
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("-10"))
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("-10"))
|
||||
assertEquals("DLC5HXnYsJAFqmM9hgPj5G8whQ4TpyE9WMBssqCayLBwA2", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("0"))
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("0"))
|
||||
assertEquals("DL4UVhGh4tqu1G86UVoGNaDDNCMsBtNHzE6BSZuNNJN7W2", keyPairZero.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("1"))
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("1"))
|
||||
assertEquals("DL8EZUdHixovcCynKMQzrMWBnXQAcbVDHi6ArPphqwJVzq", keyPairOne.public.toStringShort())
|
||||
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bits.public.toStringShort())
|
||||
// The underlying implementation uses the first 256 bytes of the entropy. Thus, 2^258-10 and 2^258-50 and 2^514-10 have the same impact.
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DLB9K1UiBrWonn481z6NzkqoWHjMBXpfDeaet3wiwRNWSU", keyPairBiggerThan512bits.public.toStringShort())
|
||||
|
||||
// Try another big number.
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(EDDSA_ED25519_SHA512, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DL5tEFVMXMGrzwjfCAW34JjkhsRkPfFyJ38iEnmpB6L2Z9", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ECDSA R1 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("10"))
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("10"))
|
||||
assertEquals("DLHDcxuSt9J3cbjd2Dsx4rAgYYA7BAP7A8VLrFiq1tH9yy", keyPairPositive.public.toStringShort())
|
||||
// The underlying implementation uses the hash of entropy if it is out of range 2 < entropy < N, where N the order of the group.
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("-10"))
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("-10"))
|
||||
assertEquals("DLBASmjiMZuu1g3EtdHJxfSueXE8PRoUWbkdU61Qcnpamt", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("0"))
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("0"))
|
||||
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZero.public.toStringShort())
|
||||
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, zeroHashed)
|
||||
assertEquals(-1, zeroHashed.compareTo((ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, zeroHashed)
|
||||
assertEquals("DLH2FEHEnsT3MpCJt2gfyNjpqRqcBxeupK4YRPXvDsVEkb", keyPairZeroHashed.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("1"))
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("1"))
|
||||
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOne.public.toStringShort())
|
||||
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, oneHashed)
|
||||
assertEquals(-1, oneHashed.compareTo((ECDSA_SECP256R1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, oneHashed)
|
||||
assertEquals("DLHrtKwjv6onq9HcrQDJPs8Cgtai5mZU5ZU6sb1ivJjx3z", keyPairOneHashed.public.toStringShort())
|
||||
|
||||
// 2 is in the range.
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2"))
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2"))
|
||||
assertEquals("DLFoz6txJ3vHcKNSM1vFxHJUoEQ69PorBwW64dHsAnEoZB", keyPairTwo.public.toStringShort())
|
||||
|
||||
// Try big numbers that are out of range.
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLBv6fZqaCTbE4L7sgjbt19biXHMgU9CzR5s8g8XBJjZ11", keyPairBiggerThan256bits.public.toStringShort())
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DLANmjhGSVdLyghxcPHrn3KuGatscf6LtvqifUDxw7SGU8", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DL9sKwMExBTD3MnJN6LWGqo496Erkebs9fxZtXLVJUBY9Z", keyPairBiggerThan512bits.public.toStringShort())
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256R1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DLBwjWwPJSF9E7b1NWaSbEJ4oK8CF7RDGWd648TiBhZoL1", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ECDSA K1 keyPair from entropy`() {
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("10"))
|
||||
val keyPairPositive = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("10"))
|
||||
assertEquals("DL6pYKUgH17az8MLdonvvUtUPN8TqwpCGcdgLr7vg3skCU", keyPairPositive.public.toStringShort())
|
||||
// The underlying implementation uses the hash of entropy if it is out of range 2 <= entropy < N, where N the order of the group.
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("-10"))
|
||||
val keyPairNegative = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("-10"))
|
||||
assertEquals("DLnpXhxece69Nyqgm3pPt3yV7ESQYDJKoYxs1hKgfBAEu", keyPairNegative.public.toStringShort())
|
||||
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("0"))
|
||||
val keyPairZero = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("0"))
|
||||
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZero.public.toStringShort())
|
||||
// BigIntenger.Zero is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val zeroHashed = BigInteger(1, BigInteger("0").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, zeroHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, zeroHashed)
|
||||
assertEquals(-1, zeroHashed.compareTo((ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairZeroHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, zeroHashed)
|
||||
assertEquals("DLBC28e18T6KsYwjTFfUWJfhvHjvYVapyVf6antnqUkbgd", keyPairZeroHashed.public.toStringShort())
|
||||
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("1"))
|
||||
val keyPairOne = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("1"))
|
||||
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOne.public.toStringShort())
|
||||
// BigIntenger.ONE is out or range, so 1 and hash(1.toByteArray) would have the same impact.
|
||||
val oneHashed = BigInteger(1, BigInteger("1").toByteArray().sha256().bytes)
|
||||
// Check oneHashed < N (order of the group), otherwise we would need an extra hash.
|
||||
assertEquals(-1, oneHashed.compareTo((Crypto.ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, oneHashed)
|
||||
assertEquals(-1, oneHashed.compareTo((ECDSA_SECP256K1_SHA256.algSpec as ECNamedCurveParameterSpec).n))
|
||||
val keyPairOneHashed = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, oneHashed)
|
||||
assertEquals("DLBimRXdEQhJUTpL6f9ri9woNdsze6mwkRrhsML13Eh7ET", keyPairOneHashed.public.toStringShort())
|
||||
|
||||
// 2 is in the range.
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2"))
|
||||
val keyPairTwo = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2"))
|
||||
assertEquals("DLG32UWaevGw9YY7w1Rf9mmK88biavgpDnJA9bG4GapVPs", keyPairTwo.public.toStringShort())
|
||||
|
||||
// Try big numbers that are out of range.
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan256bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger.TEN))
|
||||
assertEquals("DLGHsdv2xeAuM7n3sBc6mFfiphXe6VSf3YxqvviKDU6Vbd", keyPairBiggerThan256bits.public.toStringShort())
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
val keyPairBiggerThan256bitsV2 = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(258).minus(BigInteger("50")))
|
||||
assertEquals("DL9yJfiNGqteRrKPjGUkRQkeqzuQ4kwcYQWMCi5YKuUHrk", keyPairBiggerThan256bitsV2.public.toStringShort())
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
val keyPairBiggerThan512bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(514).minus(BigInteger.TEN))
|
||||
assertEquals("DL3Wr5EQGrMTaKBy5XMvG8rvSfKX1AYZLCRU8kixGbxt1E", keyPairBiggerThan512bits.public.toStringShort())
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
val keyPairBiggerThan258bits = Crypto.deriveKeyPairFromEntropy(ECDSA_SECP256K1_SHA256, BigInteger("2").pow(259).plus(BigInteger.ONE))
|
||||
assertEquals("DL7NbssqvuuJ4cqFkkaVYu9j1MsVswESGgCfbqBS9ULwuM", keyPairBiggerThan258bits.public.toStringShort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Ensure deterministic signatures of EdDSA, SPHINCS-256 and RSA PKCS1`() {
|
||||
listOf(EDDSA_ED25519_SHA512, SPHINCS256_SHA256, RSA_SHA256)
|
||||
.forEach { testDeterministicSignatures(it) }
|
||||
}
|
||||
|
||||
private fun testDeterministicSignatures(signatureScheme: SignatureScheme) {
|
||||
val privateKey = Crypto.generateKeyPair(signatureScheme).private
|
||||
val signedData1stTime = Crypto.doSign(privateKey, testBytes)
|
||||
val signedData2ndTime = Crypto.doSign(privateKey, testBytes)
|
||||
assertEquals(OpaqueBytes(signedData1stTime), OpaqueBytes(signedData2ndTime))
|
||||
|
||||
// Try for the special case of signing a zero array.
|
||||
val signedZeroArray1stTime = Crypto.doSign(privateKey, test100ZeroBytes)
|
||||
val signedZeroArray2ndTime = Crypto.doSign(privateKey, test100ZeroBytes)
|
||||
assertEquals(OpaqueBytes(signedZeroArray1stTime), OpaqueBytes(signedZeroArray2ndTime))
|
||||
|
||||
// Just in case, test that signatures of different messages are not the same.
|
||||
assertNotEquals(OpaqueBytes(signedData1stTime), OpaqueBytes(signedZeroArray1stTime))
|
||||
}
|
||||
}
|
||||
|
@ -11,23 +11,31 @@ import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
import org.bouncycastle.asn1.x509.NameConstraints
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import org.junit.Test
|
||||
import java.security.UnrecoverableKeyException
|
||||
import java.security.cert.CertPathValidator
|
||||
import java.security.cert.CertPathValidatorException
|
||||
import java.security.cert.PKIXParameters
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class X509NameConstraintsTest {
|
||||
|
||||
companion object {
|
||||
private const val storePassword = "storePassword"
|
||||
private const val keyPassword = "entryPassword"
|
||||
}
|
||||
|
||||
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<X509KeyStore, X509KeyStore> {
|
||||
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
|
||||
|
||||
val trustStore = X509KeyStore("password").apply {
|
||||
|
||||
val trustStore = X509KeyStore(storePassword).apply {
|
||||
setCertificate(X509Utilities.CORDA_ROOT_CA, rootCa.certificate)
|
||||
}
|
||||
|
||||
val keyStore = X509KeyStore("password").apply {
|
||||
val keyStore = X509KeyStore(storePassword).apply {
|
||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nodeCaCert = X509Utilities.createCertificate(
|
||||
CertificateType.NODE_CA,
|
||||
@ -43,7 +51,7 @@ class X509NameConstraintsTest {
|
||||
nodeCaKeyPair,
|
||||
X500Principal(subjectName.encoded),
|
||||
tlsKeyPair.public)
|
||||
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate))
|
||||
setPrivateKey(X509Utilities.CORDA_CLIENT_TLS, tlsKeyPair.private, listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCa.certificate), keyPassword)
|
||||
}
|
||||
|
||||
return Pair(keyStore, trustStore)
|
||||
@ -90,7 +98,6 @@ class X509NameConstraintsTest {
|
||||
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
||||
|
||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||
Crypto.ECDSA_SECP256R1_SHA256
|
||||
val pathValidator = CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME)
|
||||
|
||||
assertFailsWith(CertPathValidatorException::class) {
|
||||
@ -127,4 +134,20 @@ class X509NameConstraintsTest {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test private key retrieval`() {
|
||||
val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A")
|
||||
.map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray()
|
||||
|
||||
val nameConstraints = NameConstraints(acceptableNames, arrayOf())
|
||||
val (keystore, _) = makeKeyStores(X500Name("CN=Bank A"), nameConstraints)
|
||||
|
||||
val privateKey = keystore.getPrivateKey(X509Utilities.CORDA_CLIENT_TLS, keyPassword)
|
||||
assertEquals(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME.algorithmName, privateKey.algorithm)
|
||||
|
||||
assertFailsWith(UnrecoverableKeyException::class) {
|
||||
keystore.getPrivateKey(X509Utilities.CORDA_CLIENT_TLS, "gibberish")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ class AttachmentTests : WithMockNet {
|
||||
// Makes a node that doesn't do sanity checking at load time.
|
||||
private fun makeBadNode(name: CordaX500Name) = mockNet.createNode(
|
||||
InternalMockNodeParameters(legalName = makeUnique(name)),
|
||||
nodeFactory = { args, _ ->
|
||||
nodeFactory = { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false }
|
||||
}
|
||||
|
@ -3,15 +3,12 @@ package net.corda.core.flows
|
||||
import com.natpryce.hamkrest.*
|
||||
import com.natpryce.hamkrest.assertion.assert
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.internal.matchers.flow.willThrow
|
||||
import net.corda.core.flows.mixins.WithContracts
|
||||
import net.corda.core.flows.mixins.WithFinality
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.USD
|
||||
@ -20,9 +17,12 @@ import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyContractV2
|
||||
import net.corda.testing.contracts.DummyContractV3
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.internal.matchers.flow.willThrow
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
@ -52,59 +52,72 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
@Test
|
||||
fun `2 parties contract upgrade`() {
|
||||
// Create dummy contract.
|
||||
val signedByA = aliceNode.signDummyContract(alice.ref(1),0, bob.ref(1))
|
||||
val signedByA = aliceNode.signDummyContract(alice.ref(1), 0, bob.ref(1))
|
||||
val stx = bobNode.addSignatureTo(signedByA)
|
||||
|
||||
aliceNode.finalise(stx, bob)
|
||||
|
||||
val atx = aliceNode.getValidatedTransaction(stx)
|
||||
val btx = bobNode.getValidatedTransaction(stx)
|
||||
val aliceTx = aliceNode.getValidatedTransaction(stx)
|
||||
val bobTx = bobNode.getValidatedTransaction(stx)
|
||||
|
||||
// The request is expected to be rejected because party B hasn't authorised the upgrade yet.
|
||||
assert.that(
|
||||
aliceNode.initiateDummyContractUpgrade(atx),
|
||||
aliceNode.initiateContractUpgrade(aliceTx, DummyContractV2::class),
|
||||
willThrow<UnexpectedFlowEndException>())
|
||||
|
||||
// Party B authorise the contract state upgrade, and immediately deauthorise the same.
|
||||
assert.that(bobNode.authoriseDummyContractUpgrade(btx), willReturn())
|
||||
assert.that(bobNode.deauthoriseContractUpgrade(btx), willReturn())
|
||||
// Party B authorises the contract state upgrade, and immediately de-authorises the same.
|
||||
assert.that(bobNode.authoriseContractUpgrade(bobTx, DummyContractV2::class), willReturn())
|
||||
assert.that(bobNode.deauthoriseContractUpgrade(bobTx), willReturn())
|
||||
|
||||
// The request is expected to be rejected because party B has subsequently deauthorised a previously authorised upgrade.
|
||||
// The request is expected to be rejected because party B has subsequently de-authorised a previously authorised upgrade.
|
||||
assert.that(
|
||||
aliceNode.initiateDummyContractUpgrade(atx),
|
||||
aliceNode.initiateContractUpgrade(aliceTx, DummyContractV2::class),
|
||||
willThrow<UnexpectedFlowEndException>())
|
||||
|
||||
// Party B authorise the contract state upgrade
|
||||
assert.that(bobNode.authoriseDummyContractUpgrade(btx), willReturn())
|
||||
// Party B authorises the contract state upgrade.
|
||||
assert.that(bobNode.authoriseContractUpgrade(bobTx, DummyContractV2::class), willReturn())
|
||||
|
||||
// Party A initiates contract upgrade flow, expected to succeed this time.
|
||||
assert.that(
|
||||
aliceNode.initiateDummyContractUpgrade(atx),
|
||||
aliceNode.initiateContractUpgrade(aliceTx, DummyContractV2::class),
|
||||
willReturn(
|
||||
aliceNode.hasDummyContractUpgradeTransaction()
|
||||
and bobNode.hasDummyContractUpgradeTransaction()))
|
||||
aliceNode.hasContractUpgradeTransaction<DummyContract.State, DummyContractV2.State>()
|
||||
and bobNode.hasContractUpgradeTransaction<DummyContract.State, DummyContractV2.State>()))
|
||||
|
||||
val upgradedState = aliceNode.getStateFromVault(DummyContractV2.State::class)
|
||||
|
||||
// We now test that the upgraded state can be upgraded further, to V3.
|
||||
// Party B authorises the contract state upgrade.
|
||||
assert.that(bobNode.authoriseContractUpgrade(upgradedState, DummyContractV3::class), willReturn())
|
||||
|
||||
// Party A initiates contract upgrade flow which is expected to succeed.
|
||||
assert.that(
|
||||
aliceNode.initiateContractUpgrade(upgradedState, DummyContractV3::class),
|
||||
willReturn(
|
||||
aliceNode.hasContractUpgradeTransaction<DummyContractV2.State, DummyContractV3.State>()
|
||||
and bobNode.hasContractUpgradeTransaction<DummyContractV2.State, DummyContractV3.State>()))
|
||||
}
|
||||
|
||||
private fun TestStartedNode.issueCash(amount: Amount<Currency> = Amount(1000, USD)) =
|
||||
services.startFlow(CashIssueFlow(amount, OpaqueBytes.of(1), notary))
|
||||
.andRunNetwork()
|
||||
.resultFuture.getOrThrow()
|
||||
services.startFlow(CashIssueFlow(amount, OpaqueBytes.of(1), notary))
|
||||
.andRunNetwork()
|
||||
.resultFuture.getOrThrow()
|
||||
|
||||
private fun TestStartedNode.getBaseStateFromVault() = getStateFromVault(ContractState::class)
|
||||
|
||||
private fun TestStartedNode.getCashStateFromVault() = getStateFromVault(CashV2.State::class)
|
||||
|
||||
private fun hasIssuedAmount(expected: Amount<Issued<Currency>>) =
|
||||
hasContractState(has(CashV2.State::amount, equalTo(expected)))
|
||||
hasContractState(has(CashV2.State::amount, equalTo(expected)))
|
||||
|
||||
private fun belongsTo(vararg recipients: AbstractParty) =
|
||||
hasContractState(has(CashV2.State::owners, equalTo(recipients.toList())))
|
||||
hasContractState(has(CashV2.State::owners, equalTo(recipients.toList())))
|
||||
|
||||
private fun <T : ContractState> hasContractState(expectation: Matcher<T>) =
|
||||
has<StateAndRef<T>, T>(
|
||||
"contract state",
|
||||
{ it.state.data },
|
||||
expectation)
|
||||
has<StateAndRef<T>, T>(
|
||||
"contract state",
|
||||
{ it.state.data },
|
||||
expectation)
|
||||
|
||||
@Test
|
||||
fun `upgrade Cash to v2`() {
|
||||
@ -123,14 +136,14 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
val upgradedState = aliceNode.getCashStateFromVault()
|
||||
assert.that(upgradedState,
|
||||
hasIssuedAmount(Amount(1000000, USD) `issued by` (alice.ref(1)))
|
||||
and belongsTo(anonymisedRecipient))
|
||||
and belongsTo(anonymisedRecipient))
|
||||
|
||||
// Make sure the upgraded state can be spent
|
||||
val movedState = upgradedState.state.data.copy(amount = upgradedState.state.data.amount.times(2))
|
||||
val spendUpgradedTx = aliceNode.signInitialTransaction {
|
||||
addInputState(upgradedState)
|
||||
addOutputState(
|
||||
upgradedState.state.copy(data = movedState)
|
||||
upgradedState.state.copy(data = movedState)
|
||||
)
|
||||
addCommand(CashV2.Move(), alice.owningKey)
|
||||
}
|
||||
@ -162,35 +175,24 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||
override fun verify(tx: LedgerTransaction) {}
|
||||
}
|
||||
|
||||
//region Operations
|
||||
private fun TestStartedNode.initiateDummyContractUpgrade(tx: SignedTransaction) =
|
||||
initiateContractUpgrade(tx, DummyContractV2::class)
|
||||
|
||||
private fun TestStartedNode.authoriseDummyContractUpgrade(tx: SignedTransaction) =
|
||||
authoriseContractUpgrade(tx, DummyContractV2::class)
|
||||
//endregion
|
||||
|
||||
//region Matchers
|
||||
private fun TestStartedNode.hasDummyContractUpgradeTransaction() =
|
||||
hasContractUpgradeTransaction<DummyContract.State, DummyContractV2.State>()
|
||||
|
||||
private inline fun <reified FROM : Any, reified TO: Any> TestStartedNode.hasContractUpgradeTransaction() =
|
||||
has<StateAndRef<ContractState>, ContractUpgradeLedgerTransaction>(
|
||||
"a contract upgrade transaction",
|
||||
{ getContractUpgradeTransaction(it) },
|
||||
isUpgrade<FROM, TO>())
|
||||
private inline fun <reified FROM : Any, reified TO : Any> TestStartedNode.hasContractUpgradeTransaction() =
|
||||
has<StateAndRef<ContractState>, ContractUpgradeLedgerTransaction>(
|
||||
"a contract upgrade transaction",
|
||||
{ getContractUpgradeTransaction(it) },
|
||||
isUpgrade<FROM, TO>())
|
||||
|
||||
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
|
||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
||||
.resolveContractUpgradeTransaction(services)
|
||||
services.validatedTransactions.getTransaction(state.ref.txhash)!!
|
||||
.resolveContractUpgradeTransaction(services)
|
||||
|
||||
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
|
||||
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()
|
||||
|
||||
private inline fun <reified T: Any> isUpgradeFrom() =
|
||||
private inline fun <reified T : Any> isUpgradeFrom() =
|
||||
has<ContractUpgradeLedgerTransaction, Any>("input data", { it.inputs.single().state.data }, isA<T>(anything))
|
||||
|
||||
private inline fun <reified T: Any> isUpgradeTo() =
|
||||
private inline fun <reified T : Any> isUpgradeTo() =
|
||||
has<ContractUpgradeLedgerTransaction, Any>("output data", { it.outputs.single().data }, isA<T>(anything))
|
||||
//endregion
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package net.corda.core.flows
|
||||
|
||||
import com.natpryce.hamkrest.and
|
||||
import com.natpryce.hamkrest.assertion.assert
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.internal.matchers.flow.willThrow
|
||||
import net.corda.core.flows.mixins.WithFinality
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -12,6 +10,8 @@ import net.corda.finance.POUNDS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.issuedBy
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.internal.matchers.flow.willThrow
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
@ -21,7 +21,10 @@ import org.junit.Test
|
||||
class FinalityFlowTests : WithFinality {
|
||||
companion object {
|
||||
private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.finance.contracts.asset","net.corda.finance.schemas"))
|
||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(
|
||||
"net.corda.finance.contracts.asset",
|
||||
"net.corda.finance.schemas"
|
||||
))
|
||||
|
||||
@JvmStatic
|
||||
@AfterClass
|
||||
@ -33,7 +36,6 @@ class FinalityFlowTests : WithFinality {
|
||||
private val aliceNode = makeNode(ALICE_NAME)
|
||||
private val bobNode = makeNode(BOB_NAME)
|
||||
|
||||
private val alice = aliceNode.info.singleIdentity()
|
||||
private val bob = bobNode.info.singleIdentity()
|
||||
private val notary = mockNet.defaultNotaryIdentity
|
||||
|
||||
@ -59,11 +61,9 @@ class FinalityFlowTests : WithFinality {
|
||||
}
|
||||
|
||||
private fun TestStartedNode.signCashTransactionWith(other: Party): SignedTransaction {
|
||||
val amount = 1000.POUNDS.issuedBy(alice.ref(0))
|
||||
val amount = 1000.POUNDS.issuedBy(info.singleIdentity().ref(0))
|
||||
val builder = TransactionBuilder(notary)
|
||||
Cash().generateIssue(builder, amount, other, notary)
|
||||
|
||||
return services.signInitialTransaction(builder)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import rx.Observable
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@ -34,20 +37,6 @@ class NoAnswer(private val closure: () -> Unit = {}) : FlowLogic<Unit>() {
|
||||
override fun call() = closure()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to register a flow of type [R] against an initiating flow of type [I].
|
||||
*/
|
||||
inline fun <I : FlowLogic<*>, reified R : FlowLogic<*>> TestStartedNode.registerInitiatedFlow(initiatingFlowType: KClass<I>, crossinline construct: (session: FlowSession) -> R) {
|
||||
registerFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> construct(session) }, R::class.javaObjectType, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to register a flow of type [Answer] against an initiating flow of type [I], returning a valure of type [R].
|
||||
*/
|
||||
inline fun <I : FlowLogic<*>, reified R : Any> TestStartedNode.registerAnswer(initiatingFlowType: KClass<I>, value: R) {
|
||||
registerFlowFactory(initiatingFlowType.java, InitiatedFlowFactory.Core { session -> Answer(session, value) }, Answer::class.javaObjectType, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts data from a [Map[FlowSession, UntrustworthyData<Any>]] without performing checks and casting to [R].
|
||||
*/
|
||||
@ -112,4 +101,23 @@ inline fun <reified R : Any> FlowLogic<*>.receiveAll(session: FlowSession, varar
|
||||
|
||||
private fun Array<out Pair<FlowSession, Class<out Any>>>.enforceNoDuplicates() {
|
||||
require(this.size == this.toSet().size) { "A flow session can only appear once as argument." }
|
||||
}
|
||||
|
||||
inline fun <reified P : FlowLogic<*>> TestStartedNode.registerCordappFlowFactory(
|
||||
initiatingFlowClass: KClass<out FlowLogic<*>>,
|
||||
initiatedFlowVersion: Int = 1,
|
||||
noinline flowFactory: (FlowSession) -> P): CordaFuture<P> {
|
||||
|
||||
val observable = internals.registerInitiatedFlowFactory(
|
||||
initiatingFlowClass.java,
|
||||
P::class.java,
|
||||
InitiatedFlowFactory.CorDapp(initiatedFlowVersion, "", flowFactory),
|
||||
track = true)
|
||||
return observable.toFuture()
|
||||
}
|
||||
|
||||
fun <T : FlowLogic<*>> TestStartedNode.registerCoreFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>,
|
||||
initiatedFlowClass: Class<T>,
|
||||
flowFactory: (FlowSession) -> T , track: Boolean): Observable<T> {
|
||||
return this.internals.registerInitiatedFlowFactory(initiatingFlowClass, initiatedFlowClass, InitiatedFlowFactory.Core(flowFactory), track)
|
||||
}
|
@ -2,16 +2,18 @@ package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.natpryce.hamkrest.assertion.assert
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.core.flows.mixins.WithMockNet
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.node.internal.InternalMockNetwork
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Test
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
class ReceiveMultipleFlowTests : WithMockNet {
|
||||
@ -43,7 +45,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
|
||||
}
|
||||
}
|
||||
|
||||
nodes[1].registerInitiatedFlow(initiatingFlow::class) { session ->
|
||||
nodes[1].registerCordappFlowFactory(initiatingFlow::class) { session ->
|
||||
object : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
@ -123,4 +125,15 @@ class ReceiveMultipleFlowTests : WithMockNet {
|
||||
return double * string.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T> TestStartedNode.registerAnswer(kClass: KClass<out FlowLogic<Any>>, value1: T) {
|
||||
this.registerCordappFlowFactory(kClass) { session ->
|
||||
object : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
session.send(value1!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,14 +24,14 @@ import org.junit.After
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
// A dummy reference state contract.
|
||||
internal class RefState : Contract {
|
||||
companion object {
|
||||
val CONTRACT_ID = "net.corda.core.flows.RefState"
|
||||
const val CONTRACT_ID = "net.corda.core.flows.RefState"
|
||||
}
|
||||
|
||||
override fun verify(tx: LedgerTransaction) = Unit
|
||||
|
||||
data class State(val owner: Party, val version: Int = 0, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState {
|
||||
override val participants: List<AbstractParty> get() = listOf(owner)
|
||||
fun update() = copy(version = version + 1)
|
||||
@ -46,34 +46,32 @@ internal class CreateRefState : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities.first()
|
||||
return subFlow(FinalityFlow(
|
||||
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
addOutputState(RefState.State(ourIdentity), RefState.CONTRACT_ID)
|
||||
addCommand(RefState.Create(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
))
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
addOutputState(RefState.State(ourIdentity), RefState.CONTRACT_ID)
|
||||
addCommand(RefState.Create(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
return subFlow(FinalityFlow(stx))
|
||||
}
|
||||
}
|
||||
|
||||
// A flow to update a specific reference state.
|
||||
internal class UpdateRefState(val stateAndRef: StateAndRef<ContractState>) : FlowLogic<SignedTransaction>() {
|
||||
internal class UpdateRefState(private val stateAndRef: StateAndRef<RefState.State>) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities.first()
|
||||
return subFlow(FinalityFlow(
|
||||
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
addInputState(stateAndRef)
|
||||
addOutputState((stateAndRef.state.data as RefState.State).update(), RefState.CONTRACT_ID)
|
||||
addCommand(RefState.Update(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
))
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
addInputState(stateAndRef)
|
||||
addOutputState(stateAndRef.state.data.update(), RefState.CONTRACT_ID)
|
||||
addCommand(RefState.Update(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
return subFlow(FinalityFlow(stx))
|
||||
}
|
||||
}
|
||||
|
||||
// A set of flows to share a stateref with all other nodes in the mock network.
|
||||
internal object ShareRefState {
|
||||
@InitiatingFlow
|
||||
class Initiator(val stateAndRef: StateAndRef<ContractState>) : FlowLogic<Unit>() {
|
||||
class Initiator(private val stateAndRef: StateAndRef<ContractState>) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val sessions = serviceHub.networkMapCache.allNodes.flatMap { it.legalIdentities }.map { initiateFlow(it) }
|
||||
@ -85,7 +83,7 @@ internal object ShareRefState {
|
||||
}
|
||||
|
||||
@InitiatedBy(ShareRefState.Initiator::class)
|
||||
class Responder(val otherSession: FlowSession) : FlowLogic<Unit>() {
|
||||
class Responder(private val otherSession: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
logger.info("Receiving dependencies.")
|
||||
@ -99,7 +97,7 @@ internal object ShareRefState {
|
||||
}
|
||||
|
||||
// A flow to use a reference state in another transaction.
|
||||
internal class UseRefState(val linearId: UniqueIdentifier) : FlowLogic<SignedTransaction>() {
|
||||
internal class UseRefState(private val linearId: UniqueIdentifier) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities.first()
|
||||
@ -108,14 +106,12 @@ internal class UseRefState(val linearId: UniqueIdentifier) : FlowLogic<SignedTra
|
||||
relevancyStatus = Vault.RelevancyStatus.ALL
|
||||
)
|
||||
val referenceState = serviceHub.vaultService.queryBy<ContractState>(query).states.single()
|
||||
return subFlow(FinalityFlow(
|
||||
transaction = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
@Suppress("DEPRECATION") // To be removed when feature is finalised.
|
||||
addReferenceState(referenceState.referenced())
|
||||
addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
addCommand(DummyContract.Commands.Create(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
))
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = notary).apply {
|
||||
addReferenceState(referenceState.referenced())
|
||||
addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
addCommand(DummyContract.Commands.Create(), listOf(ourIdentity.owningKey))
|
||||
})
|
||||
return subFlow(FinalityFlow(stx))
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,5 +160,4 @@ class WithReferencedStatesFlowTests {
|
||||
val result = useRefTx.getOrThrow()
|
||||
assertEquals(updatedRefState.ref, result.tx.references.single())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -51,9 +51,13 @@ interface WithContracts : WithMockNet {
|
||||
|
||||
fun <T : UpgradedContract<*, *>> TestStartedNode.authoriseContractUpgrade(
|
||||
tx: SignedTransaction, toClass: KClass<T>) =
|
||||
startFlow(
|
||||
ContractUpgradeFlow.Authorise(tx.tx.outRef<ContractState>(0), toClass.java)
|
||||
)
|
||||
authoriseContractUpgrade(tx.tx.outRef(0), toClass)
|
||||
|
||||
fun <T : UpgradedContract<*, *>> TestStartedNode.authoriseContractUpgrade(
|
||||
stateAndRef: StateAndRef<ContractState>, toClass: KClass<T>) =
|
||||
startFlow(
|
||||
ContractUpgradeFlow.Authorise(stateAndRef, toClass.java)
|
||||
)
|
||||
|
||||
fun TestStartedNode.deauthoriseContractUpgrade(tx: SignedTransaction) = startFlow(
|
||||
ContractUpgradeFlow.Deauthorise(tx.tx.outRef<ContractState>(0).ref)
|
||||
|
@ -1,37 +1,42 @@
|
||||
package net.corda.core.flows.mixins
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.natpryce.hamkrest.MatchResult
|
||||
import com.natpryce.hamkrest.Matcher
|
||||
import com.natpryce.hamkrest.equalTo
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.TestStartedNode
|
||||
|
||||
interface WithFinality : WithMockNet {
|
||||
|
||||
//region Operations
|
||||
fun TestStartedNode.finalise(stx: SignedTransaction, vararg additionalParties: Party) =
|
||||
startFlowAndRunNetwork(FinalityFlow(stx, additionalParties.toSet()))
|
||||
fun TestStartedNode.finalise(stx: SignedTransaction, vararg additionalParties: Party): FlowStateMachine<SignedTransaction> {
|
||||
return startFlowAndRunNetwork(FinalityFlow(stx, additionalParties.toSet()))
|
||||
}
|
||||
|
||||
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction) =
|
||||
services.validatedTransactions.getTransaction(stx.id)!!
|
||||
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction {
|
||||
return services.validatedTransactions.getTransaction(stx.id)!!
|
||||
}
|
||||
|
||||
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party) =
|
||||
startFlow(::FinalityInvoker, stx, parties.toSet())
|
||||
.andRunNetwork()
|
||||
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg parties: Party): FlowHandle<SignedTransaction> {
|
||||
return startFlow(::FinalityInvoker, stx, parties.toSet()).andRunNetwork()
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Matchers
|
||||
fun visibleTo(other: TestStartedNode) = object : Matcher<SignedTransaction> {
|
||||
override val description = "has a transaction visible to ${other.info.singleIdentity()}"
|
||||
override fun invoke(actual: SignedTransaction) =
|
||||
equalTo(actual)(other.getValidatedTransaction(actual))
|
||||
override fun invoke(actual: SignedTransaction): MatchResult {
|
||||
return equalTo(actual)(other.getValidatedTransaction(actual))
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
@ -41,4 +46,4 @@ interface WithFinality : WithMockNet {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import org.bouncycastle.asn1.ASN1Integer
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.test.*
|
||||
|
||||
class CertRoleTests {
|
||||
@Test
|
||||
@ -22,4 +25,74 @@ class CertRoleTests {
|
||||
// Outside of the range of integers
|
||||
assertFailsWith<IllegalArgumentException> { CertRole.getInstance(ASN1Integer(Integer.MAX_VALUE + 1L)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check cert roles verify for various cert hierarchies`(){
|
||||
|
||||
// Testing for various certificate hierarchies (with or without NodeCA).
|
||||
// ROOT -> Intermediate Root -> Doorman -> NodeCA -> Legal Identity cert -> Confidential key cert
|
||||
// -> NodeCA -> TLS
|
||||
// -> Legal Identity cert -> Confidential key cert
|
||||
// -> TLS
|
||||
val rootSubject = X500Principal("CN=Root,O=R3 Ltd,L=London,C=GB")
|
||||
val intermediateRootSubject = X500Principal("CN=Intermediate Root,O=R3 Ltd,L=London,C=GB")
|
||||
val doormanSubject = X500Principal("CN=Doorman,O=R3 Ltd,L=London,C=GB")
|
||||
val nodeSubject = X500Principal("CN=Node,O=R3 Ltd,L=London,C=GB")
|
||||
|
||||
val rootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCert = X509Utilities.createSelfSignedCACertificate(rootSubject, rootKeyPair)
|
||||
val rootCertRole = CertRole.extract(rootCert)
|
||||
|
||||
val intermediateRootKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Note that [CertificateType.ROOT_CA] is used for both root and intermediate root.
|
||||
val intermediateRootCert = X509Utilities.createCertificate(CertificateType.ROOT_CA, rootCert, rootKeyPair, intermediateRootSubject, intermediateRootKeyPair.public)
|
||||
val intermediateRootCertRole = CertRole.extract(intermediateRootCert)
|
||||
|
||||
val doormanKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
// Note that [CertificateType.INTERMEDIATE_CA] has actually role = CertRole.DOORMAN_CA, see [CertificateType] in [X509Utilities].
|
||||
val doormanCert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateRootCert, intermediateRootKeyPair, doormanSubject, doormanKeyPair.public)
|
||||
val doormanCertRole = CertRole.extract(doormanCert)!!
|
||||
|
||||
val nodeCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val nodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, doormanCert, doormanKeyPair, nodeSubject, nodeCAKeyPair.public)
|
||||
val nodeCACertRole = CertRole.extract(nodeCACert)!!
|
||||
|
||||
val tlsKeyPairFromNodeCA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCertFromNodeCA = X509Utilities.createCertificate(CertificateType.TLS, nodeCACert, nodeCAKeyPair, nodeSubject, tlsKeyPairFromNodeCA.public)
|
||||
val tlsCertFromNodeCARole = CertRole.extract(tlsCertFromNodeCA)!!
|
||||
|
||||
val tlsKeyPairFromDoorman = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCertFromDoorman = X509Utilities.createCertificate(CertificateType.TLS, doormanCert, doormanKeyPair, nodeSubject, tlsKeyPairFromDoorman.public)
|
||||
val tlsCertFromDoormanRole = CertRole.extract(tlsCertFromDoorman)!!
|
||||
|
||||
val legalIDKeyPairFromNodeCA = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val legalIDCertFromNodeCA = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, nodeCACert, nodeCAKeyPair, nodeSubject, legalIDKeyPairFromNodeCA.public)
|
||||
val legalIDCertFromNodeCARole = CertRole.extract(legalIDCertFromNodeCA)!!
|
||||
|
||||
val legalIDKeyPairFromDoorman = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val legalIDCertFromDoorman = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, doormanCert, doormanKeyPair, nodeSubject, legalIDKeyPairFromDoorman.public)
|
||||
val legalIDCertFromDoormanRole = CertRole.extract(legalIDCertFromDoorman)!!
|
||||
|
||||
val confidentialKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val confidentialCert = X509Utilities.createCertificate(CertificateType.CONFIDENTIAL_LEGAL_IDENTITY, legalIDCertFromNodeCA, legalIDKeyPairFromNodeCA, nodeSubject, confidentialKeyPair.public)
|
||||
val confidentialCertRole = CertRole.extract(confidentialCert)!!
|
||||
|
||||
assertNull(rootCertRole)
|
||||
assertNull(intermediateRootCertRole)
|
||||
assertEquals(tlsCertFromNodeCARole, tlsCertFromDoormanRole)
|
||||
assertEquals(legalIDCertFromNodeCARole, legalIDCertFromDoormanRole)
|
||||
|
||||
assertTrue { doormanCertRole.isValidParent(intermediateRootCertRole) } // Doorman is signed by Intermediate Root.
|
||||
assertTrue { nodeCACertRole.isValidParent(doormanCertRole) } // NodeCA is signed by Doorman.
|
||||
assertTrue { tlsCertFromNodeCARole.isValidParent(nodeCACertRole) } // TLS is signed by NodeCA.
|
||||
assertTrue { tlsCertFromDoormanRole.isValidParent(doormanCertRole) } // TLS can also be signed by Doorman.
|
||||
assertTrue { legalIDCertFromNodeCARole.isValidParent(nodeCACertRole) } // Legal Identity is signed by NodeCA.
|
||||
assertTrue { legalIDCertFromDoormanRole.isValidParent(doormanCertRole) } // Legal Identity can also be signed by Doorman.
|
||||
assertTrue { confidentialCertRole.isValidParent(legalIDCertFromNodeCARole) } // Confidential key cert is signed by Legal Identity.
|
||||
|
||||
assertFalse { legalIDCertFromDoormanRole.isValidParent(tlsCertFromDoormanRole) } // Legal Identity cannot be signed by TLS.
|
||||
assertFalse { tlsCertFromNodeCARole.isValidParent(legalIDCertFromNodeCARole) } // TLS cannot be signed by Legal Identity.
|
||||
assertFalse { confidentialCertRole.isValidParent(nodeCACertRole) } // Confidential key cert cannot be signed by NodeCA.
|
||||
assertFalse { confidentialCertRole.isValidParent(doormanCertRole) } // Confidential key cert cannot be signed by Doorman.
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ class JarSignatureCollectorTest {
|
||||
private const val ALICE_PASS = "alicepass"
|
||||
private const val BOB = "bob"
|
||||
private const val BOB_PASS = "bobpass"
|
||||
private const val CHARLIE = "Charlie"
|
||||
private const val CHARLIE_PASS = "charliepass"
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
|
@ -1,9 +1,21 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NamedCacheTest {
|
||||
class NamedCacheTest : NamedCacheFactory {
|
||||
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V> {
|
||||
throw IllegalStateException("Should not be called")
|
||||
}
|
||||
|
||||
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> {
|
||||
throw IllegalStateException("Should not be called")
|
||||
}
|
||||
|
||||
fun checkNameHelper(name: String, throws: Boolean) {
|
||||
var exceptionThrown = false
|
||||
try {
|
||||
|
@ -3,16 +3,12 @@ package net.corda.core.serialization
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.TestNoSecurityDataVendingFlow
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FetchAttachmentsFlow
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import net.corda.nodeapi.internal.persistence.currentDBSession
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
@ -151,17 +147,16 @@ class AttachmentSerializationTest {
|
||||
}
|
||||
|
||||
private fun launchFlow(clientLogic: ClientLogic, rounds: Int, sendData: Boolean = false) {
|
||||
server.registerFlowFactory(
|
||||
ClientLogic::class.java,
|
||||
InitiatedFlowFactory.Core { ServerLogic(it, sendData) },
|
||||
ServerLogic::class.java,
|
||||
track = false)
|
||||
server.registerCordappFlowFactory(
|
||||
ClientLogic::class,
|
||||
1
|
||||
) { ServerLogic(it, sendData) }
|
||||
client.services.startFlow(clientLogic)
|
||||
mockNet.runNetwork(rounds)
|
||||
}
|
||||
|
||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||
client = mockNet.restartNode(client) { args, _ ->
|
||||
client = mockNet.restartNode(client) { args ->
|
||||
object : InternalMockNetwork.MockNode(args) {
|
||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.contracts.requireThat
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -21,33 +22,43 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
const val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
|
||||
|
||||
class TransactionEncumbranceTests {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private companion object {
|
||||
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
||||
val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party
|
||||
val MEGA_CORP get() = megaCorp.party
|
||||
val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
|
||||
|
||||
val defaultIssuer = MEGA_CORP.ref(1)
|
||||
|
||||
val state = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
|
||||
val stateWithNewOwner = state.copy(owner = MINI_CORP)
|
||||
val extraCashState = state.copy(amount = state.amount * 3)
|
||||
|
||||
val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z")
|
||||
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
|
||||
val timeLock = DummyTimeLock.State(FIVE_PM)
|
||||
|
||||
|
||||
val ledgerServices = MockServices(listOf("net.corda.core.transactions", "net.corda.finance.contracts.asset"), MEGA_CORP.name,
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
})
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
val defaultIssuer = MEGA_CORP.ref(1)
|
||||
|
||||
val state = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
val stateWithNewOwner = state.copy(owner = MINI_CORP)
|
||||
|
||||
val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z")
|
||||
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
|
||||
val timeLock = DummyTimeLock.State(FIVE_PM)
|
||||
|
||||
class DummyTimeLock : Contract {
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
val timeLockInput = tx.inputsOfType<State>().singleOrNull() ?: return
|
||||
@ -65,23 +76,136 @@ class TransactionEncumbranceTests {
|
||||
}
|
||||
}
|
||||
|
||||
private val ledgerServices = MockServices(listOf("net.corda.core.transactions", "net.corda.finance.contracts.asset"), MEGA_CORP.name,
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
})
|
||||
|
||||
@Test
|
||||
fun `state can be encumbered`() {
|
||||
fun `states can be bi-directionally encumbered`() {
|
||||
// Basic encumbrance example for encumbrance index links 0 -> 1 and 1 -> 0
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", 0, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
|
||||
// Full cycle example with 4 elements 0 -> 1, 1 -> 2, 2 -> 3 and 3 -> 0
|
||||
// All 3 Cash states and the TimeLock are linked and should be consumed in the same transaction.
|
||||
// Note that all of the Cash states are encumbered both together and with time lock.
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, extraCashState)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 1", encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 2", encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 3", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", 0, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
|
||||
// A transaction that includes multiple independent encumbrance chains.
|
||||
// Each Cash state is encumbered with its own TimeLock.
|
||||
// Note that all of the Cash states are encumbered both together and with time lock.
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, extraCashState)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock A", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock B", encumbrance = 4, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock C", encumbrance = 5, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock A", 0, timeLock)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock B", 1, timeLock)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock C", 2, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
|
||||
// Full cycle example with 4 elements (different combination) 0 -> 3, 1 -> 2, 2 -> 0 and 3 -> 1
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, extraCashState)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 3", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 2", encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 0", encumbrance = 0, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", 1, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non bi-directional encumbrance will fail`() {
|
||||
// Single encumbrance with no back link.
|
||||
assertFailsWith<TransactionVerificationException.TransactionNonMatchingEncumbranceException> {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Full cycle fails due to duplicate encumbrance reference.
|
||||
// 0 -> 1, 1 -> 3, 2 -> 3 (thus 3 is referenced two times).
|
||||
assertFailsWith<TransactionVerificationException.TransactionDuplicateEncumbranceException> {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 1", encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 3", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 3 again", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No Full cycle due to non-matching encumbered-encumbrance elements.
|
||||
// 0 -> 1, 1 -> 3, 2 -> 0 (thus offending indices [2, 3], because 2 is not referenced and 3 is not encumbered).
|
||||
assertFailsWith<TransactionVerificationException.TransactionNonMatchingEncumbranceException> {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 1", encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 3", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by state 0", encumbrance = 0, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No Full cycle in one of the encumbrance chains due to non-matching encumbered-encumbrance elements.
|
||||
// 0 -> 2, 2 -> 0 is valid. On the other hand, there is 1 -> 3 only and 3 -> 1 does not exist.
|
||||
// (thus offending indices [1, 3], because 1 is not referenced and 3 is not encumbered).
|
||||
assertFailsWith<TransactionVerificationException.TransactionNonMatchingEncumbranceException> {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock A", encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock B", encumbrance = 3, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock A", 0, timeLock)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock B", timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -132,7 +256,7 @@ class TransactionEncumbranceTests {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock",0, timeLock)
|
||||
}
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
@ -151,7 +275,7 @@ class TransactionEncumbranceTests {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, encumbrance = 0, contractState = stateWithNewOwner)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by itself", encumbrance = 0, contractState = stateWithNewOwner)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
this `fails with` "Missing required encumbrance 0 in OUTPUT"
|
||||
}
|
||||
@ -164,7 +288,7 @@ class TransactionEncumbranceTests {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(TEST_TIMELOCK_ID, encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "state encumbered by state 2 which does not exist", encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
this `fails with` "Missing required encumbrance 2 in OUTPUT"
|
||||
@ -178,7 +302,7 @@ class TransactionEncumbranceTests {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1, contractState = state)
|
||||
output(Cash.PROGRAM_ID, "some other state", state)
|
||||
output(Cash.PROGRAM_ID, "some other state", encumbrance = 0, contractState = state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
}
|
||||
transaction {
|
||||
|
@ -50,11 +50,11 @@ class ProgressTrackerTest {
|
||||
assertEquals(0, pt.stepIndex)
|
||||
var stepNotification: ProgressTracker.Step? = null
|
||||
pt.changes.subscribe { stepNotification = (it as? ProgressTracker.Change.Position)?.newStep }
|
||||
|
||||
assertEquals(ProgressTracker.UNSTARTED, pt.currentStep)
|
||||
assertEquals(ProgressTracker.STARTING, pt.nextStep())
|
||||
assertEquals(SimpleSteps.ONE, pt.nextStep())
|
||||
assertEquals(1, pt.stepIndex)
|
||||
assertEquals(2, pt.stepIndex)
|
||||
assertEquals(SimpleSteps.ONE, stepNotification)
|
||||
|
||||
assertEquals(SimpleSteps.TWO, pt.nextStep())
|
||||
assertEquals(SimpleSteps.THREE, pt.nextStep())
|
||||
assertEquals(SimpleSteps.FOUR, pt.nextStep())
|
||||
@ -87,8 +87,10 @@ class ProgressTrackerTest {
|
||||
assertEquals(SimpleSteps.TWO, (stepNotification.pollFirst() as ProgressTracker.Change.Structural).parent)
|
||||
assertNextStep(SimpleSteps.TWO)
|
||||
|
||||
assertEquals(pt2.currentStep, ProgressTracker.UNSTARTED)
|
||||
assertEquals(ProgressTracker.STARTING, pt2.nextStep())
|
||||
assertEquals(ChildSteps.AYY, pt2.nextStep())
|
||||
assertNextStep(ChildSteps.AYY)
|
||||
assertEquals((stepNotification.last as ProgressTracker.Change.Position).newStep, ChildSteps.AYY)
|
||||
assertEquals(ChildSteps.BEE, pt2.nextStep())
|
||||
}
|
||||
|
||||
@ -115,19 +117,19 @@ class ProgressTrackerTest {
|
||||
|
||||
// Travel tree.
|
||||
pt.currentStep = SimpleSteps.ONE
|
||||
assertCurrentStepsTree(0, SimpleSteps.ONE)
|
||||
assertCurrentStepsTree(1, SimpleSteps.ONE)
|
||||
|
||||
pt.currentStep = SimpleSteps.TWO
|
||||
assertCurrentStepsTree(1, SimpleSteps.TWO)
|
||||
assertCurrentStepsTree(2, SimpleSteps.TWO)
|
||||
|
||||
pt2.currentStep = ChildSteps.BEE
|
||||
assertCurrentStepsTree(3, ChildSteps.BEE)
|
||||
assertCurrentStepsTree(5, ChildSteps.BEE)
|
||||
|
||||
pt.currentStep = SimpleSteps.THREE
|
||||
assertCurrentStepsTree(5, SimpleSteps.THREE)
|
||||
assertCurrentStepsTree(7, SimpleSteps.THREE)
|
||||
|
||||
// Assert no structure changes and proper steps propagation.
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 3, 5))
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 2, 5, 7))
|
||||
assertThat(stepsTreeNotification).isEmpty()
|
||||
}
|
||||
|
||||
@ -153,16 +155,16 @@ class ProgressTrackerTest {
|
||||
}
|
||||
|
||||
pt.currentStep = SimpleSteps.ONE
|
||||
assertCurrentStepsTree(0, SimpleSteps.ONE)
|
||||
assertCurrentStepsTree(1, SimpleSteps.ONE)
|
||||
|
||||
pt.currentStep = SimpleSteps.FOUR
|
||||
assertCurrentStepsTree(3, SimpleSteps.FOUR)
|
||||
assertCurrentStepsTree(4, SimpleSteps.FOUR)
|
||||
|
||||
pt2.currentStep = ChildSteps.SEA
|
||||
assertCurrentStepsTree(6, ChildSteps.SEA)
|
||||
assertCurrentStepsTree(8, ChildSteps.SEA)
|
||||
|
||||
// Assert no structure changes and proper steps propagation.
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 3, 6))
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 8))
|
||||
assertThat(stepsTreeNotification).isEmpty()
|
||||
}
|
||||
|
||||
@ -189,18 +191,18 @@ class ProgressTrackerTest {
|
||||
}
|
||||
|
||||
pt.currentStep = SimpleSteps.TWO
|
||||
assertCurrentStepsTree(1, SimpleSteps.TWO)
|
||||
assertCurrentStepsTree(2, SimpleSteps.TWO)
|
||||
|
||||
pt.currentStep = SimpleSteps.FOUR
|
||||
assertCurrentStepsTree(6, SimpleSteps.FOUR)
|
||||
assertCurrentStepsTree(8, SimpleSteps.FOUR)
|
||||
|
||||
|
||||
pt.setChildProgressTracker(SimpleSteps.THREE, pt3)
|
||||
|
||||
assertCurrentStepsTree(9, SimpleSteps.FOUR)
|
||||
assertCurrentStepsTree(12, SimpleSteps.FOUR)
|
||||
|
||||
// Assert no structure changes and proper steps propagation.
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 6, 9))
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 8, 12))
|
||||
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state
|
||||
}
|
||||
|
||||
@ -228,14 +230,14 @@ class ProgressTrackerTest {
|
||||
pt.currentStep = SimpleSteps.TWO
|
||||
pt2.currentStep = ChildSteps.SEA
|
||||
pt3.currentStep = BabySteps.UNOS
|
||||
assertCurrentStepsTree(4, ChildSteps.SEA)
|
||||
assertCurrentStepsTree(6, ChildSteps.SEA)
|
||||
|
||||
pt.setChildProgressTracker(SimpleSteps.TWO, pt3)
|
||||
|
||||
assertCurrentStepsTree(2, BabySteps.UNOS)
|
||||
assertCurrentStepsTree(4, BabySteps.UNOS)
|
||||
|
||||
// Assert no structure changes and proper steps propagation.
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(1, 4, 2))
|
||||
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(2, 6, 4))
|
||||
assertThat(stepsTreeNotification).hasSize(2) // 1 change + 1 our initial state.
|
||||
}
|
||||
|
||||
|
@ -33,11 +33,10 @@ dependencies {
|
||||
|
||||
// ASM: byte code manipulation library
|
||||
compile "org.ow2.asm:asm:$asm_version"
|
||||
compile "org.ow2.asm:asm-tree:$asm_version"
|
||||
compile "org.ow2.asm:asm-commons:$asm_version"
|
||||
|
||||
// Classpath scanner
|
||||
shadow "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
|
||||
// ClassGraph: classpath scanning
|
||||
shadow "io.github.classgraph:classgraph:$class_graph_version"
|
||||
|
||||
// Test utilities
|
||||
testCompile "junit:junit:$junit_version"
|
||||
@ -52,6 +51,21 @@ shadowJar {
|
||||
baseName 'corda-djvm'
|
||||
classifier ''
|
||||
relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm'
|
||||
|
||||
// These particular classes are only needed to "bootstrap"
|
||||
// the compilation of the other sandbox classes. At runtime,
|
||||
// we will generate better versions from deterministic-rt.jar.
|
||||
exclude 'sandbox/java/lang/Appendable.class'
|
||||
exclude 'sandbox/java/lang/CharSequence.class'
|
||||
exclude 'sandbox/java/lang/Character\$*.class'
|
||||
exclude 'sandbox/java/lang/Comparable.class'
|
||||
exclude 'sandbox/java/lang/Enum.class'
|
||||
exclude 'sandbox/java/lang/Iterable.class'
|
||||
exclude 'sandbox/java/lang/StackTraceElement.class'
|
||||
exclude 'sandbox/java/lang/StringBuffer.class'
|
||||
exclude 'sandbox/java/lang/StringBuilder.class'
|
||||
exclude 'sandbox/java/nio/**'
|
||||
exclude 'sandbox/java/util/**'
|
||||
}
|
||||
assemble.dependsOn shadowJar
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
@file:JvmName("Utilities")
|
||||
package net.corda.djvm.tools.cli
|
||||
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
import java.lang.reflect.Modifier
|
||||
import io.github.classgraph.ClassGraph
|
||||
import java.lang.reflect.Modifier.isAbstract
|
||||
import java.lang.reflect.Modifier.isStatic
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
@ -92,13 +93,10 @@ val userClassPath: String = System.getProperty("java.class.path")
|
||||
* Get a reference of each concrete class that implements interface or class [T].
|
||||
*/
|
||||
inline fun <reified T> find(scanSpec: String = "net/corda/djvm"): List<Class<*>> {
|
||||
val references = mutableListOf<Class<*>>()
|
||||
FastClasspathScanner(scanSpec)
|
||||
.matchClassesImplementing(T::class.java) { clazz ->
|
||||
if (!Modifier.isAbstract(clazz.modifiers) && !Modifier.isStatic(clazz.modifiers)) {
|
||||
references.add(clazz)
|
||||
}
|
||||
}
|
||||
return ClassGraph()
|
||||
.whitelistPaths(scanSpec)
|
||||
.enableAllInfo()
|
||||
.scan()
|
||||
return references
|
||||
.use { it.getClassesImplementing(T::class.java.name).loadClasses(T::class.java) }
|
||||
.filter { !isAbstract(it.modifiers) && !isStatic(it.modifiers) }
|
||||
}
|
||||
|
19
djvm/src/main/java/sandbox/java/lang/Appendable.java
Normal file
19
djvm/src/main/java/sandbox/java/lang/Appendable.java
Normal file
@ -0,0 +1,19 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.Appendable}
|
||||
* to keep {@link sandbox.java.lang.StringBuilder}, {@link sandbox.java.lang.StringBuffer}
|
||||
* and {@link sandbox.java.lang.String} honest.
|
||||
* Note that it does not extend {@link java.lang.Appendable}.
|
||||
*/
|
||||
public interface Appendable {
|
||||
|
||||
Appendable append(CharSequence csq, int start, int end) throws IOException;
|
||||
|
||||
Appendable append(CharSequence csq) throws IOException;
|
||||
|
||||
Appendable append(char c) throws IOException;
|
||||
|
||||
}
|
100
djvm/src/main/java/sandbox/java/lang/Boolean.java
Normal file
100
djvm/src/main/java/sandbox/java/lang/Boolean.java
Normal file
@ -0,0 +1,100 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Boolean extends Object implements Comparable<Boolean>, Serializable {
|
||||
|
||||
public static final Boolean TRUE = new Boolean(true);
|
||||
public static final Boolean FALSE = new Boolean(false);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Boolean> TYPE = (Class) java.lang.Boolean.TYPE;
|
||||
|
||||
private final boolean value;
|
||||
|
||||
public Boolean(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Boolean(String s) {
|
||||
this(parseBoolean(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Boolean) && ((Boolean) other).value == value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(boolean value) {
|
||||
return java.lang.Boolean.hashCode(value);
|
||||
}
|
||||
|
||||
public boolean booleanValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Boolean.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return toString(value);
|
||||
}
|
||||
|
||||
public static String toString(boolean b) {
|
||||
return String.valueOf(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Boolean fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Boolean other) {
|
||||
return compare(value, other.value);
|
||||
}
|
||||
|
||||
public static int compare(boolean x, boolean y) {
|
||||
return java.lang.Boolean.compare(x, y);
|
||||
}
|
||||
|
||||
public static boolean parseBoolean(String s) {
|
||||
return java.lang.Boolean.parseBoolean(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static Boolean valueOf(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
public static Boolean valueOf(String s) {
|
||||
return valueOf(parseBoolean(s));
|
||||
}
|
||||
|
||||
public static boolean logicalAnd(boolean a, boolean b) {
|
||||
return java.lang.Boolean.logicalAnd(a, b);
|
||||
}
|
||||
|
||||
public static boolean logicalOr(boolean a, boolean b) {
|
||||
return java.lang.Boolean.logicalOr(a, b);
|
||||
}
|
||||
|
||||
public static boolean logicalXor(boolean a, boolean b) {
|
||||
return java.lang.Boolean.logicalXor(a, b);
|
||||
}
|
||||
|
||||
public static Boolean toDJVM(java.lang.Boolean b) { return (b == null) ? null : new Boolean(b); }
|
||||
}
|
129
djvm/src/main/java/sandbox/java/lang/Byte.java
Normal file
129
djvm/src/main/java/sandbox/java/lang/Byte.java
Normal file
@ -0,0 +1,129 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Byte extends Number implements Comparable<Byte> {
|
||||
public static final byte MIN_VALUE = java.lang.Byte.MIN_VALUE;
|
||||
public static final byte MAX_VALUE = java.lang.Byte.MAX_VALUE;
|
||||
public static final int BYTES = java.lang.Byte.BYTES;
|
||||
public static final int SIZE = java.lang.Byte.SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Byte> TYPE = (Class) java.lang.Byte.TYPE;
|
||||
|
||||
private final byte value;
|
||||
|
||||
public Byte(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Byte(String s) throws NumberFormatException {
|
||||
this.value = parseByte(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return (short) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return (double) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(byte b) {
|
||||
return java.lang.Byte.hashCode(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Byte) && ((Byte) other).value == value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Byte.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Byte fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Byte other) {
|
||||
return compare(this.value, other.value);
|
||||
}
|
||||
|
||||
public static int compare(byte x, byte y) {
|
||||
return java.lang.Byte.compare(x, y);
|
||||
}
|
||||
|
||||
public static String toString(byte b) {
|
||||
return Integer.toString(b);
|
||||
}
|
||||
|
||||
public static Byte valueOf(byte b) {
|
||||
return new Byte(b);
|
||||
}
|
||||
|
||||
public static byte parseByte(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Byte.parseByte(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static byte parseByte(String s) throws NumberFormatException {
|
||||
return java.lang.Byte.parseByte(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static Byte valueOf(String s, int radix) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Byte.valueOf(String.fromDJVM(s), radix));
|
||||
}
|
||||
|
||||
public static Byte valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Byte.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Byte decode(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Byte.decode(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static int toUnsignedInt(byte b) {
|
||||
return java.lang.Byte.toUnsignedInt(b);
|
||||
}
|
||||
|
||||
public static long toUnsignedLong(byte b) {
|
||||
return java.lang.Byte.toUnsignedLong(b);
|
||||
}
|
||||
|
||||
public static Byte toDJVM(java.lang.Byte b) {
|
||||
return (b == null) ? null : valueOf(b);
|
||||
}
|
||||
}
|
21
djvm/src/main/java/sandbox/java/lang/CharSequence.java
Normal file
21
djvm/src/main/java/sandbox/java/lang/CharSequence.java
Normal file
@ -0,0 +1,21 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.CharSequence}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public interface CharSequence extends java.lang.CharSequence {
|
||||
|
||||
@Override
|
||||
CharSequence subSequence(int start, int end);
|
||||
|
||||
@NotNull
|
||||
String toDJVMString();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.String toString();
|
||||
|
||||
}
|
481
djvm/src/main/java/sandbox/java/lang/Character.java
Normal file
481
djvm/src/main/java/sandbox/java/lang/Character.java
Normal file
@ -0,0 +1,481 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Character extends Object implements Comparable<Character>, Serializable {
|
||||
public static final int MIN_RADIX = java.lang.Character.MIN_RADIX;
|
||||
public static final int MAX_RADIX = java.lang.Character.MAX_RADIX;
|
||||
public static final char MIN_VALUE = java.lang.Character.MIN_VALUE;
|
||||
public static final char MAX_VALUE = java.lang.Character.MAX_VALUE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Character> TYPE = (Class) java.lang.Character.TYPE;
|
||||
|
||||
public static final byte UNASSIGNED = java.lang.Character.UNASSIGNED;
|
||||
public static final byte UPPERCASE_LETTER = java.lang.Character.UPPERCASE_LETTER;
|
||||
public static final byte LOWERCASE_LETTER = java.lang.Character.LOWERCASE_LETTER;
|
||||
public static final byte TITLECASE_LETTER = java.lang.Character.TITLECASE_LETTER;
|
||||
public static final byte MODIFIER_LETTER = java.lang.Character.MODIFIER_LETTER;
|
||||
public static final byte OTHER_LETTER = java.lang.Character.OTHER_LETTER;
|
||||
public static final byte NON_SPACING_MARK = java.lang.Character.NON_SPACING_MARK;
|
||||
public static final byte ENCLOSING_MARK = java.lang.Character.ENCLOSING_MARK;
|
||||
public static final byte COMBINING_SPACING_MARK = java.lang.Character.COMBINING_SPACING_MARK;
|
||||
public static final byte DECIMAL_DIGIT_NUMBER = java.lang.Character.DECIMAL_DIGIT_NUMBER;
|
||||
public static final byte LETTER_NUMBER = java.lang.Character.LETTER_NUMBER;
|
||||
public static final byte OTHER_NUMBER = java.lang.Character.OTHER_NUMBER;
|
||||
public static final byte SPACE_SEPARATOR = java.lang.Character.SPACE_SEPARATOR;
|
||||
public static final byte LINE_SEPARATOR = java.lang.Character.LINE_SEPARATOR;
|
||||
public static final byte PARAGRAPH_SEPARATOR = java.lang.Character.PARAGRAPH_SEPARATOR;
|
||||
public static final byte CONTROL = java.lang.Character.CONTROL;
|
||||
public static final byte FORMAT = java.lang.Character.FORMAT;
|
||||
public static final byte PRIVATE_USE = java.lang.Character.PRIVATE_USE;
|
||||
public static final byte SURROGATE = java.lang.Character.SURROGATE;
|
||||
public static final byte DASH_PUNCTUATION = java.lang.Character.DASH_PUNCTUATION;
|
||||
public static final byte START_PUNCTUATION = java.lang.Character.START_PUNCTUATION;
|
||||
public static final byte END_PUNCTUATION = java.lang.Character.END_PUNCTUATION;
|
||||
public static final byte CONNECTOR_PUNCTUATION = java.lang.Character.CONNECTOR_PUNCTUATION;
|
||||
public static final byte OTHER_PUNCTUATION = java.lang.Character.OTHER_PUNCTUATION;
|
||||
public static final byte MATH_SYMBOL = java.lang.Character.MATH_SYMBOL;
|
||||
public static final byte CURRENCY_SYMBOL = java.lang.Character.CURRENCY_SYMBOL;
|
||||
public static final byte MODIFIER_SYMBOL = java.lang.Character.MODIFIER_SYMBOL;
|
||||
public static final byte OTHER_SYMBOL = java.lang.Character.OTHER_SYMBOL;
|
||||
public static final byte INITIAL_QUOTE_PUNCTUATION = java.lang.Character.INITIAL_QUOTE_PUNCTUATION;
|
||||
public static final byte FINAL_QUOTE_PUNCTUATION = java.lang.Character.FINAL_QUOTE_PUNCTUATION;
|
||||
public static final byte DIRECTIONALITY_UNDEFINED = java.lang.Character.DIRECTIONALITY_UNDEFINED;
|
||||
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT;
|
||||
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT;
|
||||
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC;
|
||||
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER;
|
||||
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR;
|
||||
public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = java.lang.Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR;
|
||||
public static final byte DIRECTIONALITY_ARABIC_NUMBER = java.lang.Character.DIRECTIONALITY_ARABIC_NUMBER;
|
||||
public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = java.lang.Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR;
|
||||
public static final byte DIRECTIONALITY_NONSPACING_MARK = java.lang.Character.DIRECTIONALITY_NONSPACING_MARK;
|
||||
public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = java.lang.Character.DIRECTIONALITY_BOUNDARY_NEUTRAL;
|
||||
public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = java.lang.Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR;
|
||||
public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = java.lang.Character.DIRECTIONALITY_SEGMENT_SEPARATOR;
|
||||
public static final byte DIRECTIONALITY_WHITESPACE = java.lang.Character.DIRECTIONALITY_WHITESPACE;
|
||||
public static final byte DIRECTIONALITY_OTHER_NEUTRALS = java.lang.Character.DIRECTIONALITY_OTHER_NEUTRALS;
|
||||
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING;
|
||||
public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = java.lang.Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE;
|
||||
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING;
|
||||
public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = java.lang.Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE;
|
||||
public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = java.lang.Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT;
|
||||
public static final char MIN_HIGH_SURROGATE = java.lang.Character.MIN_HIGH_SURROGATE;
|
||||
public static final char MAX_HIGH_SURROGATE = java.lang.Character.MAX_HIGH_SURROGATE;
|
||||
public static final char MIN_LOW_SURROGATE = java.lang.Character.MIN_LOW_SURROGATE;
|
||||
public static final char MAX_LOW_SURROGATE = java.lang.Character.MAX_LOW_SURROGATE;
|
||||
public static final char MIN_SURROGATE = java.lang.Character.MIN_SURROGATE;
|
||||
public static final char MAX_SURROGATE = java.lang.Character.MAX_SURROGATE;
|
||||
public static final int MIN_SUPPLEMENTARY_CODE_POINT = java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
|
||||
public static final int MIN_CODE_POINT = java.lang.Character.MIN_CODE_POINT;
|
||||
public static final int MAX_CODE_POINT = java.lang.Character.MAX_CODE_POINT;
|
||||
public static final int BYTES = java.lang.Character.BYTES;
|
||||
public static final int SIZE = java.lang.Character.SIZE;
|
||||
|
||||
private final char value;
|
||||
|
||||
public Character(char c) {
|
||||
this.value = c;
|
||||
}
|
||||
|
||||
public char charValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(this.value);
|
||||
}
|
||||
|
||||
public static int hashCode(char value) {
|
||||
return java.lang.Character.hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Character) && ((Character) other).value == value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Character.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Character fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Character var1) {
|
||||
return compare(this.value, var1.value);
|
||||
}
|
||||
|
||||
public static int compare(char x, char y) {
|
||||
return java.lang.Character.compare(x, y);
|
||||
}
|
||||
|
||||
public static String toString(char c) {
|
||||
return String.toDJVM(java.lang.Character.toString(c));
|
||||
}
|
||||
|
||||
public static Character valueOf(char c) {
|
||||
return (c <= 127) ? Cache.cache[(int)c] : new Character(c);
|
||||
}
|
||||
|
||||
public static boolean isValidCodePoint(int codePoint) {
|
||||
return java.lang.Character.isValidCodePoint(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isBmpCodePoint(int codePoint) {
|
||||
return java.lang.Character.isBmpCodePoint(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isSupplementaryCodePoint(int codePoint) {
|
||||
return java.lang.Character.isSupplementaryCodePoint(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isHighSurrogate(char ch) {
|
||||
return java.lang.Character.isHighSurrogate(ch);
|
||||
}
|
||||
|
||||
public static boolean isLowSurrogate(char ch) {
|
||||
return java.lang.Character.isLowSurrogate(ch);
|
||||
}
|
||||
|
||||
public static boolean isSurrogate(char ch) {
|
||||
return java.lang.Character.isSurrogate(ch);
|
||||
}
|
||||
|
||||
public static boolean isSurrogatePair(char high, char low) {
|
||||
return java.lang.Character.isSurrogatePair(high, low);
|
||||
}
|
||||
|
||||
public static int charCount(int codePoint) {
|
||||
return java.lang.Character.charCount(codePoint);
|
||||
}
|
||||
|
||||
public static int toCodePoint(char high, char low) {
|
||||
return java.lang.Character.toCodePoint(high, low);
|
||||
}
|
||||
|
||||
public static int codePointAt(CharSequence seq, int index) {
|
||||
return java.lang.Character.codePointAt(seq, index);
|
||||
}
|
||||
|
||||
public static int codePointAt(char[] a, int index) {
|
||||
return java.lang.Character.codePointAt(a, index);
|
||||
}
|
||||
|
||||
public static int codePointAt(char[] a, int index, int limit) {
|
||||
return java.lang.Character.codePointAt(a, index, limit);
|
||||
}
|
||||
|
||||
public static int codePointBefore(CharSequence seq, int index) {
|
||||
return java.lang.Character.codePointBefore(seq, index);
|
||||
}
|
||||
|
||||
public static int codePointBefore(char[] a, int index) {
|
||||
return java.lang.Character.codePointBefore(a, index);
|
||||
}
|
||||
|
||||
public static int codePointBefore(char[] a, int index, int limit) {
|
||||
return java.lang.Character.codePointBefore(a, index, limit);
|
||||
}
|
||||
|
||||
public static char highSurrogate(int codePoint) {
|
||||
return java.lang.Character.highSurrogate(codePoint);
|
||||
}
|
||||
|
||||
public static char lowSurrogate(int codePoint) {
|
||||
return java.lang.Character.lowSurrogate(codePoint);
|
||||
}
|
||||
|
||||
public static int toChars(int codePoint, char[] dst, int dstIndex) {
|
||||
return java.lang.Character.toChars(codePoint, dst, dstIndex);
|
||||
}
|
||||
|
||||
public static char[] toChars(int codePoint) {
|
||||
return java.lang.Character.toChars(codePoint);
|
||||
}
|
||||
|
||||
public static int codePointCount(CharSequence seq, int beginIndex, int endIndex) {
|
||||
return java.lang.Character.codePointCount(seq, beginIndex, endIndex);
|
||||
}
|
||||
|
||||
public static int codePointCount(char[] a, int offset, int count) {
|
||||
return java.lang.Character.codePointCount(a, offset, count);
|
||||
}
|
||||
|
||||
public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) {
|
||||
return java.lang.Character.offsetByCodePoints(seq, index, codePointOffset);
|
||||
}
|
||||
|
||||
public static int offsetByCodePoints(char[] a, int start, int count, int index, int codePointOffset) {
|
||||
return java.lang.Character.offsetByCodePoints(a, start, count, index, codePointOffset);
|
||||
}
|
||||
|
||||
public static boolean isLowerCase(char ch) {
|
||||
return java.lang.Character.isLowerCase(ch);
|
||||
}
|
||||
|
||||
public static boolean isLowerCase(int codePoint) {
|
||||
return java.lang.Character.isLowerCase(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isUpperCase(char ch) {
|
||||
return java.lang.Character.isUpperCase(ch);
|
||||
}
|
||||
|
||||
public static boolean isUpperCase(int codePoint) {
|
||||
return java.lang.Character.isUpperCase(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isTitleCase(char ch) {
|
||||
return java.lang.Character.isTitleCase(ch);
|
||||
}
|
||||
|
||||
public static boolean isTitleCase(int codePoint) {
|
||||
return java.lang.Character.isTitleCase(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isDigit(char ch) {
|
||||
return java.lang.Character.isDigit(ch);
|
||||
}
|
||||
|
||||
public static boolean isDigit(int codePoint) {
|
||||
return java.lang.Character.isDigit(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isDefined(char ch) {
|
||||
return java.lang.Character.isDefined(ch);
|
||||
}
|
||||
|
||||
public static boolean isDefined(int codePoint) {
|
||||
return java.lang.Character.isDefined(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isLetter(char ch) {
|
||||
return java.lang.Character.isLetter(ch);
|
||||
}
|
||||
|
||||
public static boolean isLetter(int codePoint) {
|
||||
return java.lang.Character.isLetter(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isLetterOrDigit(char ch) {
|
||||
return java.lang.Character.isLetterOrDigit(ch);
|
||||
}
|
||||
|
||||
public static boolean isLetterOrDigit(int codePoint) {
|
||||
return java.lang.Character.isLetterOrDigit(codePoint);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJavaLetter(char ch) {
|
||||
return java.lang.Character.isJavaLetter(ch);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJavaLetterOrDigit(char ch) {
|
||||
return java.lang.Character.isJavaLetterOrDigit(ch);
|
||||
}
|
||||
|
||||
public static boolean isAlphabetic(int codePoint) {
|
||||
return java.lang.Character.isAlphabetic(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isIdeographic(int codePoint) {
|
||||
return java.lang.Character.isIdeographic(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isJavaIdentifierStart(char ch) {
|
||||
return java.lang.Character.isJavaIdentifierStart(ch);
|
||||
}
|
||||
|
||||
public static boolean isJavaIdentifierStart(int codePoint) {
|
||||
return java.lang.Character.isJavaIdentifierStart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isJavaIdentifierPart(char ch) {
|
||||
return java.lang.Character.isJavaIdentifierPart(ch);
|
||||
}
|
||||
|
||||
public static boolean isJavaIdentifierPart(int codePoint) {
|
||||
return java.lang.Character.isJavaIdentifierPart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isUnicodeIdentifierStart(char ch) {
|
||||
return java.lang.Character.isUnicodeIdentifierStart(ch);
|
||||
}
|
||||
|
||||
public static boolean isUnicodeIdentifierStart(int codePoint) {
|
||||
return java.lang.Character.isUnicodeIdentifierStart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isUnicodeIdentifierPart(char ch) {
|
||||
return java.lang.Character.isUnicodeIdentifierPart(ch);
|
||||
}
|
||||
|
||||
public static boolean isUnicodeIdentifierPart(int codePoint) {
|
||||
return java.lang.Character.isUnicodeIdentifierPart(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isIdentifierIgnorable(char ch) {
|
||||
return java.lang.Character.isIdentifierIgnorable(ch);
|
||||
}
|
||||
|
||||
public static boolean isIdentifierIgnorable(int codePoint) {
|
||||
return java.lang.Character.isIdentifierIgnorable(codePoint);
|
||||
}
|
||||
|
||||
public static char toLowerCase(char ch) {
|
||||
return java.lang.Character.toLowerCase(ch);
|
||||
}
|
||||
|
||||
public static int toLowerCase(int codePoint) {
|
||||
return java.lang.Character.toLowerCase(codePoint);
|
||||
}
|
||||
|
||||
public static char toUpperCase(char ch) {
|
||||
return java.lang.Character.toUpperCase(ch);
|
||||
}
|
||||
|
||||
public static int toUpperCase(int codePoint) {
|
||||
return java.lang.Character.toUpperCase(codePoint);
|
||||
}
|
||||
|
||||
public static char toTitleCase(char ch) {
|
||||
return java.lang.Character.toTitleCase(ch);
|
||||
}
|
||||
|
||||
public static int toTitleCase(int codePoint) {
|
||||
return java.lang.Character.toTitleCase(codePoint);
|
||||
}
|
||||
|
||||
public static int digit(char ch, int radix) {
|
||||
return java.lang.Character.digit(ch, radix);
|
||||
}
|
||||
|
||||
public static int digit(int codePoint, int radix) {
|
||||
return java.lang.Character.digit(codePoint, radix);
|
||||
}
|
||||
|
||||
public static int getNumericValue(char ch) {
|
||||
return java.lang.Character.getNumericValue(ch);
|
||||
}
|
||||
|
||||
public static int getNumericValue(int codePoint) {
|
||||
return java.lang.Character.getNumericValue(codePoint);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isSpace(char ch) {
|
||||
return java.lang.Character.isSpace(ch);
|
||||
}
|
||||
|
||||
public static boolean isSpaceChar(char ch) {
|
||||
return java.lang.Character.isSpaceChar(ch);
|
||||
}
|
||||
|
||||
public static boolean isSpaceChar(int codePoint) {
|
||||
return java.lang.Character.isSpaceChar(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isWhitespace(char ch) {
|
||||
return java.lang.Character.isWhitespace(ch);
|
||||
}
|
||||
|
||||
public static boolean isWhitespace(int codePoint) {
|
||||
return java.lang.Character.isWhitespace(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isISOControl(char ch) {
|
||||
return java.lang.Character.isISOControl(ch);
|
||||
}
|
||||
|
||||
public static boolean isISOControl(int codePoint) {
|
||||
return java.lang.Character.isISOControl(codePoint);
|
||||
}
|
||||
|
||||
public static int getType(char ch) {
|
||||
return java.lang.Character.getType(ch);
|
||||
}
|
||||
|
||||
public static int getType(int codePoint) {
|
||||
return java.lang.Character.getType(codePoint);
|
||||
}
|
||||
|
||||
public static char forDigit(int digit, int radix) {
|
||||
return java.lang.Character.forDigit(digit, radix);
|
||||
}
|
||||
|
||||
public static byte getDirectionality(char ch) {
|
||||
return java.lang.Character.getDirectionality(ch);
|
||||
}
|
||||
|
||||
public static byte getDirectionality(int codePoint) {
|
||||
return java.lang.Character.getDirectionality(codePoint);
|
||||
}
|
||||
|
||||
public static boolean isMirrored(char ch) {
|
||||
return java.lang.Character.isMirrored(ch);
|
||||
}
|
||||
|
||||
public static boolean isMirrored(int codePoint) {
|
||||
return java.lang.Character.isMirrored(codePoint);
|
||||
}
|
||||
|
||||
public static String getName(int codePoint) {
|
||||
return String.toDJVM(java.lang.Character.getName(codePoint));
|
||||
}
|
||||
|
||||
public static Character toDJVM(java.lang.Character c) {
|
||||
return (c == null) ? null : valueOf(c);
|
||||
}
|
||||
|
||||
// These three nested classes are placeholders to ensure that
|
||||
// the Character class bytecode is generated correctly. The
|
||||
// real classes will be loaded from the from the bootstrap jar
|
||||
// and then mapped into the sandbox.* namespace.
|
||||
public static final class UnicodeScript extends Enum<UnicodeScript> {
|
||||
private UnicodeScript(String name, int index) {
|
||||
super(name, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull UnicodeScript other) {
|
||||
throw new UnsupportedOperationException("Bootstrap implementation");
|
||||
}
|
||||
}
|
||||
public static final class UnicodeBlock extends Subset {}
|
||||
public static class Subset extends Object {}
|
||||
|
||||
/**
|
||||
* Keep pre-allocated instances of the first 128 characters
|
||||
* on the basis that these will be used most frequently.
|
||||
*/
|
||||
private static class Cache {
|
||||
private static final Character[] cache = new Character[128];
|
||||
|
||||
static {
|
||||
for (int c = 0; c < cache.length; ++c) {
|
||||
cache[c] = new Character((char) c);
|
||||
}
|
||||
}
|
||||
|
||||
private Cache() {}
|
||||
}
|
||||
}
|
8
djvm/src/main/java/sandbox/java/lang/Comparable.java
Normal file
8
djvm/src/main/java/sandbox/java/lang/Comparable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.Comparable}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public interface Comparable<T> extends java.lang.Comparable<T> {
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Pinned exceptions inherit from {@link java.lang.Throwable}, but we
|
||||
* still need to be able to pass them through the sandbox's
|
||||
* exception handlers. In which case we will wrap them inside
|
||||
* one of these.
|
||||
*
|
||||
* Exceptions wrapped inside one of these cannot be caught.
|
||||
*
|
||||
* Also used for passing exceptions through finally blocks without
|
||||
* any expensive unwrapping to {@link sandbox.java.lang.Throwable}
|
||||
* based types.
|
||||
*/
|
||||
final class DJVMThrowableWrapper extends Throwable {
|
||||
private final java.lang.Throwable throwable;
|
||||
|
||||
DJVMThrowableWrapper(java.lang.Throwable t) {
|
||||
throwable = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent this wrapper from creating its own stack trace.
|
||||
*/
|
||||
@Override
|
||||
public final Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
final java.lang.Throwable fromDJVM() {
|
||||
return throwable;
|
||||
}
|
||||
}
|
163
djvm/src/main/java/sandbox/java/lang/Double.java
Normal file
163
djvm/src/main/java/sandbox/java/lang/Double.java
Normal file
@ -0,0 +1,163 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Double extends Number implements Comparable<Double> {
|
||||
public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY;
|
||||
public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY;
|
||||
public static final double NaN = java.lang.Double.NaN;
|
||||
public static final double MAX_VALUE = java.lang.Double.MAX_VALUE;
|
||||
public static final double MIN_NORMAL = java.lang.Double.MIN_NORMAL;
|
||||
public static final double MIN_VALUE = java.lang.Double.MIN_VALUE;
|
||||
public static final int MAX_EXPONENT = java.lang.Double.MAX_EXPONENT;
|
||||
public static final int MIN_EXPONENT = java.lang.Double.MIN_EXPONENT;
|
||||
public static final int BYTES = java.lang.Double.BYTES;
|
||||
public static final int SIZE = java.lang.Double.SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Double> TYPE = (Class) java.lang.Double.TYPE;
|
||||
|
||||
private final double value;
|
||||
|
||||
public Double(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Double(String s) throws NumberFormatException {
|
||||
this.value = parseDouble(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return (short)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
public boolean isNaN() {
|
||||
return java.lang.Double.isNaN(value);
|
||||
}
|
||||
|
||||
public boolean isInfinite() {
|
||||
return isInfinite(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Double) && doubleToLongBits(((Double)other).value) == doubleToLongBits(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(double d) {
|
||||
return java.lang.Double.hashCode(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Double.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Double fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Double other) {
|
||||
return compare(this.value, other.value);
|
||||
}
|
||||
|
||||
public static String toString(double d) {
|
||||
return String.toDJVM(java.lang.Double.toString(d));
|
||||
}
|
||||
|
||||
public static String toHexString(double d) {
|
||||
return String.toDJVM(java.lang.Double.toHexString(d));
|
||||
}
|
||||
|
||||
public static Double valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Double.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Double valueOf(double d) {
|
||||
return new Double(d);
|
||||
}
|
||||
|
||||
public static double parseDouble(String s) throws NumberFormatException {
|
||||
return java.lang.Double.parseDouble(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static boolean isNaN(double d) {
|
||||
return java.lang.Double.isNaN(d);
|
||||
}
|
||||
|
||||
public static boolean isInfinite(double d) {
|
||||
return java.lang.Double.isInfinite(d);
|
||||
}
|
||||
|
||||
public static boolean isFinite(double d) {
|
||||
return java.lang.Double.isFinite(d);
|
||||
}
|
||||
|
||||
public static long doubleToLongBits(double d) {
|
||||
return java.lang.Double.doubleToLongBits(d);
|
||||
}
|
||||
|
||||
public static long doubleToRawLongBits(double d) {
|
||||
return java.lang.Double.doubleToRawLongBits(d);
|
||||
}
|
||||
|
||||
public static double longBitsToDouble(long bits) {
|
||||
return java.lang.Double.longBitsToDouble(bits);
|
||||
}
|
||||
|
||||
public static int compare(double d1, double d2) {
|
||||
return java.lang.Double.compare(d1, d2);
|
||||
}
|
||||
|
||||
public static double sum(double a, double b) {
|
||||
return java.lang.Double.sum(a, b);
|
||||
}
|
||||
|
||||
public static double max(double a, double b) {
|
||||
return java.lang.Double.max(a, b);
|
||||
}
|
||||
|
||||
public static double min(double a, double b) {
|
||||
return java.lang.Double.min(a, b);
|
||||
}
|
||||
|
||||
public static Double toDJVM(java.lang.Double d) {
|
||||
return (d == null) ? null : valueOf(d);
|
||||
}
|
||||
}
|
35
djvm/src/main/java/sandbox/java/lang/Enum.java
Normal file
35
djvm/src/main/java/sandbox/java/lang/Enum.java
Normal file
@ -0,0 +1,35 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This is a dummy class. We will load the actual Enum class at run-time.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable {
|
||||
|
||||
private final String name;
|
||||
private final int ordinal;
|
||||
|
||||
protected Enum(String name, int ordinal) {
|
||||
this.name = name;
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int ordinal() {
|
||||
return ordinal;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
final java.lang.Enum<?> fromDJVM() {
|
||||
throw new UnsupportedOperationException("Dummy implementation");
|
||||
}
|
||||
|
||||
}
|
163
djvm/src/main/java/sandbox/java/lang/Float.java
Normal file
163
djvm/src/main/java/sandbox/java/lang/Float.java
Normal file
@ -0,0 +1,163 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Float extends Number implements Comparable<Float> {
|
||||
public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY;
|
||||
public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY;
|
||||
public static final float NaN = java.lang.Float.NaN;
|
||||
public static final float MAX_VALUE = java.lang.Float.MAX_VALUE;
|
||||
public static final float MIN_NORMAL = java.lang.Float.MIN_NORMAL;
|
||||
public static final float MIN_VALUE = java.lang.Float.MIN_VALUE;
|
||||
public static final int MAX_EXPONENT = java.lang.Float.MAX_EXPONENT;
|
||||
public static final int MIN_EXPONENT = java.lang.Float.MIN_EXPONENT;
|
||||
public static final int BYTES = java.lang.Float.BYTES;
|
||||
public static final int SIZE = java.lang.Float.SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Float> TYPE = (Class) java.lang.Float.TYPE;
|
||||
|
||||
private final float value;
|
||||
|
||||
public Float(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Float(String s) throws NumberFormatException {
|
||||
this.value = parseFloat(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(float f) {
|
||||
return java.lang.Float.hashCode(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return other instanceof Float && floatToIntBits(((Float)other).value) == floatToIntBits(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Float.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Float fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return (double)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return (short)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Float other) {
|
||||
return compare(this.value, other.value);
|
||||
}
|
||||
|
||||
public boolean isNaN() {
|
||||
return isNaN(value);
|
||||
}
|
||||
|
||||
public boolean isInfinite() {
|
||||
return isInfinite(value);
|
||||
}
|
||||
|
||||
public static String toString(float f) {
|
||||
return String.valueOf(f);
|
||||
}
|
||||
|
||||
public static String toHexString(float f) {
|
||||
return String.toDJVM(java.lang.Float.toHexString(f));
|
||||
}
|
||||
|
||||
public static Float valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Float.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Float valueOf(float f) {
|
||||
return new Float(f);
|
||||
}
|
||||
|
||||
public static float parseFloat(String s) throws NumberFormatException {
|
||||
return java.lang.Float.parseFloat(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static boolean isNaN(float f) {
|
||||
return java.lang.Float.isNaN(f);
|
||||
}
|
||||
|
||||
public static boolean isInfinite(float f) {
|
||||
return java.lang.Float.isInfinite(f);
|
||||
}
|
||||
|
||||
public static boolean isFinite(float f) {
|
||||
return java.lang.Float.isFinite(f);
|
||||
}
|
||||
|
||||
public static int floatToIntBits(float f) {
|
||||
return java.lang.Float.floatToIntBits(f);
|
||||
}
|
||||
|
||||
public static int floatToRawIntBits(float f) {
|
||||
return java.lang.Float.floatToIntBits(f);
|
||||
}
|
||||
|
||||
public static float intBitsToFloat(int bits) {
|
||||
return java.lang.Float.intBitsToFloat(bits);
|
||||
}
|
||||
|
||||
public static int compare(float f1, float f2) {
|
||||
return java.lang.Float.compare(f1, f2);
|
||||
}
|
||||
|
||||
public static float sum(float a, float b) {
|
||||
return java.lang.Float.sum(a, b);
|
||||
}
|
||||
|
||||
public static float max(float a, float b) {
|
||||
return java.lang.Float.max(a, b);
|
||||
}
|
||||
|
||||
public static float min(float a, float b) {
|
||||
return java.lang.Float.min(a, b);
|
||||
}
|
||||
|
||||
public static Float toDJVM(java.lang.Float f) {
|
||||
return (f == null) ? null : valueOf(f);
|
||||
}
|
||||
}
|
241
djvm/src/main/java/sandbox/java/lang/Integer.java
Normal file
241
djvm/src/main/java/sandbox/java/lang/Integer.java
Normal file
@ -0,0 +1,241 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Integer extends Number implements Comparable<Integer> {
|
||||
|
||||
public static final int MIN_VALUE = java.lang.Integer.MIN_VALUE;
|
||||
public static final int MAX_VALUE = java.lang.Integer.MAX_VALUE;
|
||||
public static final int BYTES = java.lang.Integer.BYTES;
|
||||
public static final int SIZE = java.lang.Integer.SIZE;
|
||||
|
||||
static final int[] SIZE_TABLE = new int[] { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, MAX_VALUE };
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Integer> TYPE = (Class) java.lang.Integer.TYPE;
|
||||
|
||||
private final int value;
|
||||
|
||||
public Integer(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Integer(String s) throws NumberFormatException {
|
||||
this.value = parseInt(s, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Integer.hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(int i) {
|
||||
return java.lang.Integer.hashCode(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Integer) && (value == ((Integer) other).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return (short) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return (byte) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return (double) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Integer other) {
|
||||
return compare(this.value, other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Integer.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Integer fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String toString(int i, int radix) {
|
||||
return String.toDJVM(java.lang.Integer.toString(i, radix));
|
||||
}
|
||||
|
||||
public static String toUnsignedString(int i, int radix) {
|
||||
return String.toDJVM(java.lang.Integer.toUnsignedString(i, radix));
|
||||
}
|
||||
|
||||
public static String toHexString(int i) {
|
||||
return String.toDJVM(java.lang.Integer.toHexString(i));
|
||||
}
|
||||
|
||||
public static String toOctalString(int i) {
|
||||
return String.toDJVM(java.lang.Integer.toOctalString(i));
|
||||
}
|
||||
|
||||
public static String toBinaryString(int i) {
|
||||
return String.toDJVM(java.lang.Integer.toBinaryString(i));
|
||||
}
|
||||
|
||||
public static String toString(int i) {
|
||||
return String.toDJVM(java.lang.Integer.toString(i));
|
||||
}
|
||||
|
||||
public static String toUnsignedString(int i) {
|
||||
return String.toDJVM(java.lang.Integer.toUnsignedString(i));
|
||||
}
|
||||
|
||||
public static int parseInt(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Integer.parseInt(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static int parseInt(String s) throws NumberFormatException {
|
||||
return java.lang.Integer.parseInt(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static int parseUnsignedInt(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Integer.parseUnsignedInt(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static int parseUnsignedInt(String s) throws NumberFormatException {
|
||||
return java.lang.Integer.parseUnsignedInt(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static Integer valueOf(String s, int radix) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Integer.valueOf(String.fromDJVM(s), radix));
|
||||
}
|
||||
|
||||
public static Integer valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Integer.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Integer valueOf(int i) {
|
||||
return new Integer(i);
|
||||
}
|
||||
|
||||
public static Integer decode(String nm) throws NumberFormatException {
|
||||
return new Integer(java.lang.Integer.decode(String.fromDJVM(nm)));
|
||||
}
|
||||
|
||||
public static int compare(int x, int y) {
|
||||
return java.lang.Integer.compare(x, y);
|
||||
}
|
||||
|
||||
public static int compareUnsigned(int x, int y) {
|
||||
return java.lang.Integer.compareUnsigned(x, y);
|
||||
}
|
||||
|
||||
public static long toUnsignedLong(int x) {
|
||||
return java.lang.Integer.toUnsignedLong(x);
|
||||
}
|
||||
|
||||
public static int divideUnsigned(int dividend, int divisor) {
|
||||
return java.lang.Integer.divideUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public static int remainderUnsigned(int dividend, int divisor) {
|
||||
return java.lang.Integer.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public static int highestOneBit(int i) {
|
||||
return java.lang.Integer.highestOneBit(i);
|
||||
}
|
||||
|
||||
public static int lowestOneBit(int i) {
|
||||
return java.lang.Integer.lowestOneBit(i);
|
||||
}
|
||||
|
||||
public static int numberOfLeadingZeros(int i) {
|
||||
return java.lang.Integer.numberOfLeadingZeros(i);
|
||||
}
|
||||
|
||||
public static int numberOfTrailingZeros(int i) {
|
||||
return java.lang.Integer.numberOfTrailingZeros(i);
|
||||
}
|
||||
|
||||
public static int bitCount(int i) {
|
||||
return java.lang.Integer.bitCount(i);
|
||||
}
|
||||
|
||||
public static int rotateLeft(int i, int distance) {
|
||||
return java.lang.Integer.rotateLeft(i, distance);
|
||||
}
|
||||
|
||||
public static int rotateRight(int i, int distance) {
|
||||
return java.lang.Integer.rotateRight(i, distance);
|
||||
}
|
||||
|
||||
public static int reverse(int i) {
|
||||
return java.lang.Integer.reverse(i);
|
||||
}
|
||||
|
||||
public static int signum(int i) {
|
||||
return java.lang.Integer.signum(i);
|
||||
}
|
||||
|
||||
public static int reverseBytes(int i) {
|
||||
return java.lang.Integer.reverseBytes(i);
|
||||
}
|
||||
|
||||
public static int sum(int a, int b) {
|
||||
return java.lang.Integer.sum(a, b);
|
||||
}
|
||||
|
||||
public static int max(int a, int b) {
|
||||
return java.lang.Integer.max(a, b);
|
||||
}
|
||||
|
||||
public static int min(int a, int b) {
|
||||
return java.lang.Integer.min(a, b);
|
||||
}
|
||||
|
||||
public static Integer toDJVM(java.lang.Integer i) {
|
||||
return (i == null) ? null : valueOf(i);
|
||||
}
|
||||
|
||||
static int stringSize(final int number) {
|
||||
int i = 0;
|
||||
while (number > SIZE_TABLE[i]) {
|
||||
++i;
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
static void getChars(final int number, int index, char[] buffer) {
|
||||
java.lang.String s = java.lang.Integer.toString(number);
|
||||
int length = s.length();
|
||||
|
||||
while (length > 0) {
|
||||
buffer[--index] = s.charAt(--length);
|
||||
}
|
||||
}
|
||||
}
|
15
djvm/src/main/java/sandbox/java/lang/Iterable.java
Normal file
15
djvm/src/main/java/sandbox/java/lang/Iterable.java
Normal file
@ -0,0 +1,15 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.Iterable}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public interface Iterable<T> extends java.lang.Iterable<T> {
|
||||
@Override
|
||||
@NotNull
|
||||
Iterator<T> iterator();
|
||||
}
|
239
djvm/src/main/java/sandbox/java/lang/Long.java
Normal file
239
djvm/src/main/java/sandbox/java/lang/Long.java
Normal file
@ -0,0 +1,239 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Long extends Number implements Comparable<Long> {
|
||||
|
||||
public static final long MIN_VALUE = java.lang.Long.MIN_VALUE;
|
||||
public static final long MAX_VALUE = java.lang.Long.MAX_VALUE;
|
||||
public static final int BYTES = java.lang.Long.BYTES;
|
||||
public static final int SIZE = java.lang.Long.SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Long> TYPE = (Class) java.lang.Long.TYPE;
|
||||
|
||||
private final long value;
|
||||
|
||||
public Long(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Long(String s) throws NumberFormatException {
|
||||
this.value = parseLong(s, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Long) && ((Long) other).longValue() == value;
|
||||
}
|
||||
|
||||
public static int hashCode(long l) {
|
||||
return java.lang.Long.hashCode(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return (short) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return (byte) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return (double) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Long other) {
|
||||
return compare(value, other.value);
|
||||
}
|
||||
|
||||
public static int compare(long x, long y) {
|
||||
return java.lang.Long.compare(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Long fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Long.toString(value);
|
||||
}
|
||||
|
||||
public static String toString(long l) {
|
||||
return String.toDJVM(java.lang.Long.toString(l));
|
||||
}
|
||||
|
||||
public static String toString(long l, int radix) {
|
||||
return String.toDJVM(java.lang.Long.toString(l, radix));
|
||||
}
|
||||
|
||||
public static String toUnsignedString(long l, int radix) {
|
||||
return String.toDJVM(java.lang.Long.toUnsignedString(l, radix));
|
||||
}
|
||||
|
||||
public static String toUnsignedString(long l) {
|
||||
return String.toDJVM(java.lang.Long.toUnsignedString(l));
|
||||
}
|
||||
|
||||
public static String toHexString(long l) {
|
||||
return String.toDJVM(java.lang.Long.toHexString(l));
|
||||
}
|
||||
|
||||
public static String toOctalString(long l) {
|
||||
return String.toDJVM(java.lang.Long.toOctalString(l));
|
||||
}
|
||||
|
||||
public static String toBinaryString(long l) {
|
||||
return String.toDJVM(java.lang.Long.toBinaryString(l));
|
||||
}
|
||||
|
||||
public static long parseLong(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Long.parseLong(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static long parseLong(String s) throws NumberFormatException {
|
||||
return java.lang.Long.parseLong(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static long parseUnsignedLong(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Long.parseUnsignedLong(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static long parseUnsignedLong(String s) throws NumberFormatException {
|
||||
return java.lang.Long.parseUnsignedLong(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static Long valueOf(String s, int radix) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Long.valueOf(String.fromDJVM(s), radix));
|
||||
}
|
||||
|
||||
public static Long valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Long.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Long valueOf(long l) {
|
||||
return new Long(l);
|
||||
}
|
||||
|
||||
public static Long decode(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Long.decode(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static int compareUnsigned(long x, long y) {
|
||||
return java.lang.Long.compareUnsigned(x, y);
|
||||
}
|
||||
|
||||
public static long divideUnsigned(long dividend, long divisor) {
|
||||
return java.lang.Long.divideUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public static long remainderUnsigned(long dividend, long divisor) {
|
||||
return java.lang.Long.remainderUnsigned(dividend, divisor);
|
||||
}
|
||||
|
||||
public static long highestOneBit(long l) {
|
||||
return java.lang.Long.highestOneBit(l);
|
||||
}
|
||||
|
||||
public static long lowestOneBit(long l) {
|
||||
return java.lang.Long.lowestOneBit(l);
|
||||
}
|
||||
|
||||
public static int numberOfLeadingZeros(long l) {
|
||||
return java.lang.Long.numberOfLeadingZeros(l);
|
||||
}
|
||||
|
||||
public static int numberOfTrailingZeros(long l) {
|
||||
return java.lang.Long.numberOfTrailingZeros(l);
|
||||
}
|
||||
|
||||
public static int bitCount(long l) {
|
||||
return java.lang.Long.bitCount(l);
|
||||
}
|
||||
|
||||
public static long rotateLeft(long i, int distance) {
|
||||
return java.lang.Long.rotateLeft(i, distance);
|
||||
}
|
||||
|
||||
public static long rotateRight(long i, int distance) {
|
||||
return java.lang.Long.rotateRight(i, distance);
|
||||
}
|
||||
|
||||
public static long reverse(long l) {
|
||||
return java.lang.Long.reverse(l);
|
||||
}
|
||||
|
||||
public static int signum(long l) {
|
||||
return java.lang.Long.signum(l);
|
||||
}
|
||||
|
||||
public static long reverseBytes(long l) {
|
||||
return java.lang.Long.reverseBytes(l);
|
||||
}
|
||||
|
||||
public static long sum(long a, long b) {
|
||||
return java.lang.Long.sum(a, b);
|
||||
}
|
||||
|
||||
public static long max(long a, long b) {
|
||||
return java.lang.Long.max(a, b);
|
||||
}
|
||||
|
||||
public static long min(long a, long b) {
|
||||
return java.lang.Long.min(a, b);
|
||||
}
|
||||
|
||||
public static Long toDJVM(java.lang.Long l) {
|
||||
return (l == null) ? null : valueOf(l);
|
||||
}
|
||||
|
||||
static int stringSize(final long number) {
|
||||
long l = 10;
|
||||
int i = 1;
|
||||
|
||||
while ((i < 19) && (number >= l)) {
|
||||
l *= 10;
|
||||
++i;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void getChars(final long number, int index, char[] buffer) {
|
||||
java.lang.String s = java.lang.Long.toString(number);
|
||||
int length = s.length();
|
||||
|
||||
while (length > 0) {
|
||||
buffer[--index] = s.charAt(--length);
|
||||
}
|
||||
}
|
||||
}
|
21
djvm/src/main/java/sandbox/java/lang/Number.java
Normal file
21
djvm/src/main/java/sandbox/java/lang/Number.java
Normal file
@ -0,0 +1,21 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Number extends Object implements Serializable {
|
||||
|
||||
public abstract double doubleValue();
|
||||
public abstract float floatValue();
|
||||
public abstract long longValue();
|
||||
public abstract int intValue();
|
||||
public abstract short shortValue();
|
||||
public abstract byte byteValue();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return String.toDJVM(toString());
|
||||
}
|
||||
}
|
70
djvm/src/main/java/sandbox/java/lang/Object.java
Normal file
70
djvm/src/main/java/sandbox/java/lang/Object.java
Normal file
@ -0,0 +1,70 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sandbox.net.corda.djvm.rules.RuleViolationError;
|
||||
|
||||
public class Object {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return sandbox.java.lang.System.identityHashCode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return toDJVMString().toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return String.toDJVM("sandbox.java.lang.Object@" + java.lang.Integer.toString(hashCode(), 16));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
java.lang.Object fromDJVM() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static java.lang.Object[] fromDJVM(java.lang.Object[] args) {
|
||||
if (args == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
java.lang.Object[] unwrapped = (java.lang.Object[]) java.lang.reflect.Array.newInstance(
|
||||
fromDJVM(args.getClass().getComponentType()), args.length
|
||||
);
|
||||
int i = 0;
|
||||
for (java.lang.Object arg : args) {
|
||||
unwrapped[i] = unwrap(arg);
|
||||
++i;
|
||||
}
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
private static java.lang.Object unwrap(java.lang.Object arg) {
|
||||
if (arg instanceof Object) {
|
||||
return ((Object) arg).fromDJVM();
|
||||
} else if (Object[].class.isAssignableFrom(arg.getClass())) {
|
||||
return fromDJVM((Object[]) arg);
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> fromDJVM(Class<?> type) {
|
||||
try {
|
||||
return DJVM.fromDJVMType(type);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuleViolationError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static java.util.Locale fromDJVM(sandbox.java.util.Locale locale) {
|
||||
return java.util.Locale.forLanguageTag(locale.toLanguageTag().fromDJVM());
|
||||
}
|
||||
|
||||
static java.nio.charset.Charset fromDJVM(sandbox.java.nio.charset.Charset charset) {
|
||||
return java.nio.charset.Charset.forName(charset.name().fromDJVM());
|
||||
}
|
||||
}
|
27
djvm/src/main/java/sandbox/java/lang/Runtime.java
Normal file
27
djvm/src/main/java/sandbox/java/lang/Runtime.java
Normal file
@ -0,0 +1,27 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class Runtime extends Object {
|
||||
private static final Runtime RUNTIME = new Runtime();
|
||||
|
||||
private Runtime() {}
|
||||
|
||||
public static Runtime getRuntime() {
|
||||
return RUNTIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Everything inside the sandbox is single-threaded.
|
||||
* @return 1
|
||||
*/
|
||||
public int availableProcessors() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void loadLibrary(String libraryName) {}
|
||||
|
||||
public void load(String fileName) {}
|
||||
|
||||
public void runFinalization() {}
|
||||
public void gc() {}
|
||||
}
|
128
djvm/src/main/java/sandbox/java/lang/Short.java
Normal file
128
djvm/src/main/java/sandbox/java/lang/Short.java
Normal file
@ -0,0 +1,128 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public final class Short extends Number implements Comparable<Short> {
|
||||
public static final short MIN_VALUE = java.lang.Short.MIN_VALUE;
|
||||
public static final short MAX_VALUE = java.lang.Short.MAX_VALUE;
|
||||
public static final int BYTES = java.lang.Short.BYTES;
|
||||
public static final int SIZE = java.lang.Short.SIZE;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final Class<Short> TYPE = (Class) java.lang.Short.TYPE;
|
||||
|
||||
private final short value;
|
||||
|
||||
public Short(short value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Short(String s) throws NumberFormatException {
|
||||
this.value = parseShort(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte byteValue() {
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short shortValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return (double)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return java.lang.Integer.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Short fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(value);
|
||||
}
|
||||
|
||||
public static int hashCode(short value) {
|
||||
return java.lang.Short.hashCode(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof Short) && ((Short) other).value == value;
|
||||
}
|
||||
|
||||
public int compareTo(@NotNull Short other) {
|
||||
return compare(this.value, other.value);
|
||||
}
|
||||
|
||||
public static int compare(short x, short y) {
|
||||
return java.lang.Short.compare(x, y);
|
||||
}
|
||||
|
||||
public static short reverseBytes(short value) {
|
||||
return java.lang.Short.reverseBytes(value);
|
||||
}
|
||||
|
||||
public static int toUnsignedInt(short x) {
|
||||
return java.lang.Short.toUnsignedInt(x);
|
||||
}
|
||||
|
||||
public static long toUnsignedLong(short x) {
|
||||
return java.lang.Short.toUnsignedLong(x);
|
||||
}
|
||||
|
||||
public static short parseShort(String s, int radix) throws NumberFormatException {
|
||||
return java.lang.Short.parseShort(String.fromDJVM(s), radix);
|
||||
}
|
||||
|
||||
public static short parseShort(String s) throws NumberFormatException {
|
||||
return java.lang.Short.parseShort(String.fromDJVM(s));
|
||||
}
|
||||
|
||||
public static Short valueOf(String s, int radix) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Short.valueOf(String.fromDJVM(s), radix));
|
||||
}
|
||||
|
||||
public static Short valueOf(String s) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Short.valueOf(String.fromDJVM(s)));
|
||||
}
|
||||
|
||||
public static Short valueOf(short s) {
|
||||
return new Short(s);
|
||||
}
|
||||
|
||||
public static Short decode(String nm) throws NumberFormatException {
|
||||
return toDJVM(java.lang.Short.decode(String.fromDJVM(nm)));
|
||||
}
|
||||
|
||||
public static Short toDJVM(java.lang.Short i) {
|
||||
return (i == null) ? null : valueOf(i);
|
||||
}
|
||||
}
|
46
djvm/src/main/java/sandbox/java/lang/StackTraceElement.java
Normal file
46
djvm/src/main/java/sandbox/java/lang/StackTraceElement.java
Normal file
@ -0,0 +1,46 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This is a dummy class. We will load the genuine class at runtime.
|
||||
*/
|
||||
public final class StackTraceElement extends Object implements java.io.Serializable {
|
||||
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final String fileName;
|
||||
private final int lineNumber;
|
||||
|
||||
public StackTraceElement(String className, String methodName, String fileName, int lineNumber) {
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.fileName = fileName;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return String.toDJVM(
|
||||
className.toString() + ':' + methodName.toString()
|
||||
+ (fileName != null ? '(' + fileName.toString() + ':' + lineNumber + ')' : "")
|
||||
);
|
||||
}
|
||||
}
|
425
djvm/src/main/java/sandbox/java/lang/String.java
Normal file
425
djvm/src/main/java/sandbox/java/lang/String.java
Normal file
@ -0,0 +1,425 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sandbox.java.nio.charset.Charset;
|
||||
import sandbox.java.util.Comparator;
|
||||
import sandbox.java.util.Locale;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class String extends Object implements Comparable<String>, CharSequence, Serializable {
|
||||
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
|
||||
|
||||
private static class CaseInsensitiveComparator extends Object implements Comparator<String>, Serializable {
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
return java.lang.String.CASE_INSENSITIVE_ORDER.compare(String.fromDJVM(s1), String.fromDJVM(s2));
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TRUE = new String("true");
|
||||
private static final String FALSE = new String("false");
|
||||
|
||||
private static final Map<java.lang.String, String> INTERNAL = new java.util.HashMap<>();
|
||||
private static final Constructor SHARED;
|
||||
|
||||
static {
|
||||
try {
|
||||
SHARED = java.lang.String.class.getDeclaredConstructor(char[].class, java.lang.Boolean.TYPE);
|
||||
SHARED.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new NoSuchMethodError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private final java.lang.String value;
|
||||
|
||||
public String() {
|
||||
this.value = "";
|
||||
}
|
||||
|
||||
public String(java.lang.String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String(char value[]) {
|
||||
this.value = new java.lang.String(value);
|
||||
}
|
||||
|
||||
public String(char value[], int offset, int count) {
|
||||
this.value = new java.lang.String(value, offset, count);
|
||||
}
|
||||
|
||||
public String(int[] codePoints, int offset, int count) {
|
||||
this.value = new java.lang.String(codePoints, offset, count);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String(byte ascii[], int hibyte, int offset, int count) {
|
||||
this.value = new java.lang.String(ascii, hibyte, offset, count);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String(byte ascii[], int hibyte) {
|
||||
this.value = new java.lang.String(ascii, hibyte);
|
||||
}
|
||||
|
||||
public String(byte bytes[], int offset, int length, String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
this.value = new java.lang.String(bytes, offset, length, fromDJVM(charsetName));
|
||||
}
|
||||
|
||||
public String(byte bytes[], int offset, int length, Charset charset) {
|
||||
this.value = new java.lang.String(bytes, offset, length, fromDJVM(charset));
|
||||
}
|
||||
|
||||
public String(byte bytes[], String charsetName)
|
||||
throws UnsupportedEncodingException {
|
||||
this.value = new java.lang.String(bytes, fromDJVM(charsetName));
|
||||
}
|
||||
|
||||
public String(byte bytes[], Charset charset) {
|
||||
this.value = new java.lang.String(bytes, fromDJVM(charset));
|
||||
}
|
||||
|
||||
public String(byte bytes[], int offset, int length) {
|
||||
this.value = new java.lang.String(bytes, offset, length);
|
||||
}
|
||||
|
||||
public String(byte bytes[]) {
|
||||
this.value = new java.lang.String(bytes);
|
||||
}
|
||||
|
||||
public String(StringBuffer buffer) {
|
||||
this.value = buffer.toString();
|
||||
}
|
||||
|
||||
public String(StringBuilder builder) {
|
||||
this.value = builder.toString();
|
||||
}
|
||||
|
||||
String(char[] value, boolean share) {
|
||||
java.lang.String newValue;
|
||||
try {
|
||||
// This is (presumably) an optimisation for memory usage.
|
||||
newValue = (java.lang.String) SHARED.newInstance(value, share);
|
||||
} catch (Exception e) {
|
||||
newValue = new java.lang.String(value);
|
||||
}
|
||||
this.value = newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return value.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return value.length();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
public int codePointAt(int index) {
|
||||
return value.codePointAt(index);
|
||||
}
|
||||
|
||||
public int codePointBefore(int index) {
|
||||
return value.codePointBefore(index);
|
||||
}
|
||||
|
||||
public int codePointCount(int beginIndex, int endIndex) {
|
||||
return value.codePointCount(beginIndex, endIndex);
|
||||
}
|
||||
|
||||
public int offsetByCodePoints(int index, int codePointOffset) {
|
||||
return value.offsetByCodePoints(index, codePointOffset);
|
||||
}
|
||||
|
||||
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
|
||||
value.getChars(srcBegin, srcEnd, dst, dstBegin);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
|
||||
value.getBytes(srcBegin, srcEnd, dst, dstBegin);
|
||||
}
|
||||
|
||||
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
|
||||
return value.getBytes(fromDJVM(charsetName));
|
||||
}
|
||||
|
||||
public byte[] getBytes(Charset charset) {
|
||||
return value.getBytes(fromDJVM(charset));
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return value.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
return (other instanceof String) && ((String) other).value.equals(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public java.lang.String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.String fromDJVM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean contentEquals(StringBuffer sb) {
|
||||
return value.contentEquals((CharSequence) sb);
|
||||
}
|
||||
|
||||
public boolean contentEquals(CharSequence cs) {
|
||||
return value.contentEquals(cs);
|
||||
}
|
||||
|
||||
public boolean equalsIgnoreCase(String anotherString) {
|
||||
return value.equalsIgnoreCase(fromDJVM(anotherString));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return toDJVM((java.lang.String) value.subSequence(start, end));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull String other) {
|
||||
return value.compareTo(other.toString());
|
||||
}
|
||||
|
||||
public int compareToIgnoreCase(String str) {
|
||||
return value.compareToIgnoreCase(fromDJVM(str));
|
||||
}
|
||||
|
||||
public boolean regionMatches(int toffset, String other, int ooffset, int len) {
|
||||
return value.regionMatches(toffset, fromDJVM(other), ooffset, len);
|
||||
}
|
||||
|
||||
public boolean regionMatches(boolean ignoreCase, int toffset,
|
||||
String other, int ooffset, int len) {
|
||||
return value.regionMatches(ignoreCase, toffset, fromDJVM(other), ooffset, len);
|
||||
}
|
||||
|
||||
public boolean startsWith(String prefix, int toffset) {
|
||||
return value.startsWith(fromDJVM(prefix), toffset);
|
||||
}
|
||||
|
||||
public boolean startsWith(String prefix) {
|
||||
return value.startsWith(fromDJVM(prefix));
|
||||
}
|
||||
|
||||
public boolean endsWith(String suffix) {
|
||||
return value.endsWith(fromDJVM(suffix));
|
||||
}
|
||||
|
||||
public int indexOf(int ch) {
|
||||
return value.indexOf(ch);
|
||||
}
|
||||
|
||||
public int indexOf(int ch, int fromIndex) {
|
||||
return value.indexOf(ch, fromIndex);
|
||||
}
|
||||
|
||||
public int lastIndexOf(int ch) {
|
||||
return value.lastIndexOf(ch);
|
||||
}
|
||||
|
||||
public int lastIndexOf(int ch, int fromIndex) {
|
||||
return value.lastIndexOf(ch, fromIndex);
|
||||
}
|
||||
|
||||
public int indexOf(String str) {
|
||||
return value.indexOf(fromDJVM(str));
|
||||
}
|
||||
|
||||
public int indexOf(String str, int fromIndex) {
|
||||
return value.indexOf(fromDJVM(str), fromIndex);
|
||||
}
|
||||
|
||||
public int lastIndexOf(String str) {
|
||||
return value.lastIndexOf(fromDJVM(str));
|
||||
}
|
||||
|
||||
public int lastIndexOf(String str, int fromIndex) {
|
||||
return value.lastIndexOf(fromDJVM(str), fromIndex);
|
||||
}
|
||||
|
||||
public String substring(int beginIndex) {
|
||||
return toDJVM(value.substring(beginIndex));
|
||||
}
|
||||
|
||||
public String substring(int beginIndex, int endIndex) {
|
||||
return toDJVM(value.substring(beginIndex, endIndex));
|
||||
}
|
||||
|
||||
public String concat(String str) {
|
||||
return toDJVM(value.concat(fromDJVM(str)));
|
||||
}
|
||||
|
||||
public String replace(char oldChar, char newChar) {
|
||||
return toDJVM(value.replace(oldChar, newChar));
|
||||
}
|
||||
|
||||
public boolean matches(String regex) {
|
||||
return value.matches(fromDJVM(regex));
|
||||
}
|
||||
|
||||
public boolean contains(CharSequence s) {
|
||||
return value.contains(s);
|
||||
}
|
||||
|
||||
public String replaceFirst(String regex, String replacement) {
|
||||
return toDJVM(value.replaceFirst(fromDJVM(regex), fromDJVM(replacement)));
|
||||
}
|
||||
|
||||
public String replaceAll(String regex, String replacement) {
|
||||
return toDJVM(value.replaceAll(fromDJVM(regex), fromDJVM(replacement)));
|
||||
}
|
||||
|
||||
public String replace(CharSequence target, CharSequence replacement) {
|
||||
return toDJVM(value.replace(target, replacement));
|
||||
}
|
||||
|
||||
public String[] split(String regex, int limit) {
|
||||
return toDJVM(value.split(fromDJVM(regex), limit));
|
||||
}
|
||||
|
||||
public String[] split(String regex) {
|
||||
return toDJVM(value.split(fromDJVM(regex)));
|
||||
}
|
||||
|
||||
public String toLowerCase(Locale locale) {
|
||||
return toDJVM(value.toLowerCase(fromDJVM(locale)));
|
||||
}
|
||||
|
||||
public String toLowerCase() {
|
||||
return toDJVM(value.toLowerCase());
|
||||
}
|
||||
|
||||
public String toUpperCase(Locale locale) {
|
||||
return toDJVM(value.toUpperCase(fromDJVM(locale)));
|
||||
}
|
||||
|
||||
public String toUpperCase() {
|
||||
return toDJVM(value.toUpperCase());
|
||||
}
|
||||
|
||||
public String trim() {
|
||||
return toDJVM(value.trim());
|
||||
}
|
||||
|
||||
public String intern() { return INTERNAL.computeIfAbsent(value, s -> this); }
|
||||
|
||||
public char[] toCharArray() {
|
||||
return value.toCharArray();
|
||||
}
|
||||
|
||||
public static String format(String format, java.lang.Object... args) {
|
||||
return toDJVM(java.lang.String.format(fromDJVM(format), fromDJVM(args)));
|
||||
}
|
||||
|
||||
public static String format(Locale locale, String format, java.lang.Object... args) {
|
||||
return toDJVM(java.lang.String.format(fromDJVM(locale), fromDJVM(format), fromDJVM(args)));
|
||||
}
|
||||
|
||||
public static String join(CharSequence delimiter, CharSequence... elements) {
|
||||
return toDJVM(java.lang.String.join(delimiter, elements));
|
||||
}
|
||||
|
||||
public static String join(CharSequence delimiter,
|
||||
Iterable<? extends CharSequence> elements) {
|
||||
return toDJVM(java.lang.String.join(delimiter, elements));
|
||||
}
|
||||
|
||||
public static String valueOf(java.lang.Object obj) {
|
||||
return (obj instanceof Object) ? ((Object) obj).toDJVMString() : toDJVM(java.lang.String.valueOf(obj));
|
||||
}
|
||||
|
||||
public static String valueOf(char data[]) {
|
||||
return toDJVM(java.lang.String.valueOf(data));
|
||||
}
|
||||
|
||||
public static String valueOf(char data[], int offset, int count) {
|
||||
return toDJVM(java.lang.String.valueOf(data, offset, count));
|
||||
}
|
||||
|
||||
public static String copyValueOf(char data[], int offset, int count) {
|
||||
return toDJVM(java.lang.String.copyValueOf(data, offset, count));
|
||||
}
|
||||
|
||||
public static String copyValueOf(char data[]) {
|
||||
return toDJVM(java.lang.String.copyValueOf(data));
|
||||
}
|
||||
|
||||
public static String valueOf(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
public static String valueOf(char c) {
|
||||
return toDJVM(java.lang.String.valueOf(c));
|
||||
}
|
||||
|
||||
public static String valueOf(int i) {
|
||||
return toDJVM(java.lang.String.valueOf(i));
|
||||
}
|
||||
|
||||
public static String valueOf(long l) {
|
||||
return toDJVM(java.lang.String.valueOf(l));
|
||||
}
|
||||
|
||||
public static String valueOf(float f) {
|
||||
return toDJVM(java.lang.String.valueOf(f));
|
||||
}
|
||||
|
||||
public static String valueOf(double d) {
|
||||
return toDJVM(java.lang.String.valueOf(d));
|
||||
}
|
||||
|
||||
static String[] toDJVM(java.lang.String[] value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String[] result = new String[value.length];
|
||||
int i = 0;
|
||||
for (java.lang.String v : value) {
|
||||
result[i] = toDJVM(v);
|
||||
++i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String toDJVM(java.lang.String value) {
|
||||
return (value == null) ? null : new String(value);
|
||||
}
|
||||
|
||||
public static java.lang.String fromDJVM(String value) {
|
||||
return (value == null) ? null : value.fromDJVM();
|
||||
}
|
||||
}
|
20
djvm/src/main/java/sandbox/java/lang/StringBuffer.java
Normal file
20
djvm/src/main/java/sandbox/java/lang/StringBuffer.java
Normal file
@ -0,0 +1,20 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.StringBuffer}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public abstract class StringBuffer extends Object implements CharSequence, Appendable, Serializable {
|
||||
|
||||
@Override
|
||||
public abstract StringBuffer append(CharSequence seq);
|
||||
|
||||
@Override
|
||||
public abstract StringBuffer append(CharSequence seq, int start, int end);
|
||||
|
||||
@Override
|
||||
public abstract StringBuffer append(char c);
|
||||
|
||||
}
|
20
djvm/src/main/java/sandbox/java/lang/StringBuilder.java
Normal file
20
djvm/src/main/java/sandbox/java/lang/StringBuilder.java
Normal file
@ -0,0 +1,20 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.StringBuilder}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public abstract class StringBuilder extends Object implements Appendable, CharSequence, Serializable {
|
||||
|
||||
@Override
|
||||
public abstract StringBuilder append(CharSequence seq);
|
||||
|
||||
@Override
|
||||
public abstract StringBuilder append(CharSequence seq, int start, int end);
|
||||
|
||||
@Override
|
||||
public abstract StringBuilder append(char c);
|
||||
|
||||
}
|
28
djvm/src/main/java/sandbox/java/lang/System.java
Normal file
28
djvm/src/main/java/sandbox/java/lang/System.java
Normal file
@ -0,0 +1,28 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public final class System extends Object {
|
||||
|
||||
private System() {}
|
||||
|
||||
/*
|
||||
* This class is duplicated into every sandbox, where everything is single-threaded.
|
||||
*/
|
||||
private static final java.util.Map<java.lang.Integer, java.lang.Integer> objectHashCodes = new java.util.LinkedHashMap<>();
|
||||
private static int objectCounter = 0;
|
||||
|
||||
public static int identityHashCode(java.lang.Object obj) {
|
||||
int nativeHashCode = java.lang.System.identityHashCode(obj);
|
||||
// TODO Instead of using a magic offset below, one could take in a per-context seed
|
||||
return objectHashCodes.computeIfAbsent(nativeHashCode, i -> ++objectCounter + 0xfed_c0de);
|
||||
}
|
||||
|
||||
public static final String lineSeparator = String.toDJVM("\n");
|
||||
|
||||
public static void arraycopy(java.lang.Object src, int srcPos, java.lang.Object dest, int destPos, int length) {
|
||||
java.lang.System.arraycopy(src, srcPos, dest, destPos, length);
|
||||
}
|
||||
|
||||
public static void runFinalization() {}
|
||||
public static void gc() {}
|
||||
}
|
59
djvm/src/main/java/sandbox/java/lang/ThreadLocal.java
Normal file
59
djvm/src/main/java/sandbox/java/lang/ThreadLocal.java
Normal file
@ -0,0 +1,59 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import sandbox.java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Everything inside the sandbox is single-threaded, so this
|
||||
* implementation of ThreadLocal is sufficient.
|
||||
* @param <T> Underlying type of this thread-local variable.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public class ThreadLocal<T> extends Object {
|
||||
|
||||
private T value;
|
||||
private boolean isSet;
|
||||
|
||||
public ThreadLocal() {
|
||||
}
|
||||
|
||||
protected T initialValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
if (!isSet) {
|
||||
set(initialValue());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void set(T value) {
|
||||
this.value = value;
|
||||
this.isSet = true;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
value = null;
|
||||
isSet = false;
|
||||
}
|
||||
|
||||
public static <V> ThreadLocal<V> withInitial(Supplier<? extends V> supplier) {
|
||||
return new SuppliedThreadLocal<>(supplier);
|
||||
}
|
||||
|
||||
// Stub class for compiling ThreadLocal. The sandbox will import the
|
||||
// actual SuppliedThreadLocal class at run-time. Having said that, we
|
||||
// still need a working implementation here for the sake of our tests.
|
||||
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
|
||||
private final Supplier<? extends T> supplier;
|
||||
|
||||
SuppliedThreadLocal(Supplier<? extends T> supplier) {
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T initialValue() {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
}
|
137
djvm/src/main/java/sandbox/java/lang/Throwable.java
Normal file
137
djvm/src/main/java/sandbox/java/lang/Throwable.java
Normal file
@ -0,0 +1,137 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sandbox.TaskTypes;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
public class Throwable extends Object implements Serializable {
|
||||
private static final StackTraceElement[] NO_STACK_TRACE = new StackTraceElement[0];
|
||||
|
||||
private String message;
|
||||
private Throwable cause;
|
||||
private StackTraceElement[] stackTrace;
|
||||
|
||||
public Throwable() {
|
||||
this.cause = this;
|
||||
fillInStackTrace();
|
||||
}
|
||||
|
||||
public Throwable(String message) {
|
||||
this();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Throwable(Throwable cause) {
|
||||
this.cause = cause;
|
||||
this.message = (cause == null) ? null : cause.toDJVMString();
|
||||
fillInStackTrace();
|
||||
}
|
||||
|
||||
public Throwable(String message, Throwable cause) {
|
||||
this.message = message;
|
||||
this.cause = cause;
|
||||
fillInStackTrace();
|
||||
}
|
||||
|
||||
protected Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
if (writableStackTrace) {
|
||||
fillInStackTrace();
|
||||
} else {
|
||||
stackTrace = NO_STACK_TRACE;
|
||||
}
|
||||
this.message = message;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getLocalizedMessage() {
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return (cause == this) ? null : cause;
|
||||
}
|
||||
|
||||
public Throwable initCause(Throwable cause) {
|
||||
if (this.cause != this) {
|
||||
throw new java.lang.IllegalStateException(
|
||||
"Can't overwrite cause with " + java.util.Objects.toString(cause, "a null"), fromDJVM());
|
||||
}
|
||||
if (cause == this) {
|
||||
throw new java.lang.IllegalArgumentException("Self-causation not permitted", fromDJVM());
|
||||
}
|
||||
this.cause = cause;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String toDJVMString() {
|
||||
java.lang.String s = getClass().getName();
|
||||
String localized = getLocalizedMessage();
|
||||
return String.valueOf((localized != null) ? (s + ": " + localized.toString()) : s);
|
||||
}
|
||||
|
||||
public StackTraceElement[] getStackTrace() {
|
||||
return (stackTrace == NO_STACK_TRACE) ? stackTrace : stackTrace.clone();
|
||||
}
|
||||
|
||||
public void setStackTrace(StackTraceElement[] stackTrace) {
|
||||
StackTraceElement[] traceCopy = stackTrace.clone();
|
||||
|
||||
for (int i = 0; i < traceCopy.length; ++i) {
|
||||
if (traceCopy[i] == null) {
|
||||
throw new java.lang.NullPointerException("stackTrace[" + i + ']');
|
||||
}
|
||||
}
|
||||
|
||||
this.stackTrace = traceCopy;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"ThrowableNotThrown", "UnusedReturnValue"})
|
||||
public Throwable fillInStackTrace() {
|
||||
if (stackTrace == null) {
|
||||
/*
|
||||
* We have been invoked from within this exception's constructor.
|
||||
* Work our way up the stack trace until we find this constructor,
|
||||
* and then find out who actually invoked it. This is where our
|
||||
* sandboxed stack trace will start from.
|
||||
*
|
||||
* Our stack trace will end at the point where we entered the sandbox.
|
||||
*/
|
||||
final java.lang.StackTraceElement[] elements = new java.lang.Throwable().getStackTrace();
|
||||
final java.lang.String exceptionName = getClass().getName();
|
||||
int startIdx = 1;
|
||||
while (startIdx < elements.length && !isConstructorFor(elements[startIdx], exceptionName)) {
|
||||
++startIdx;
|
||||
}
|
||||
while (startIdx < elements.length && isConstructorFor(elements[startIdx], exceptionName)) {
|
||||
++startIdx;
|
||||
}
|
||||
|
||||
int endIdx = startIdx;
|
||||
while (endIdx < elements.length && !TaskTypes.isEntryPoint(elements[endIdx])) {
|
||||
++endIdx;
|
||||
}
|
||||
stackTrace = (startIdx == elements.length) ? NO_STACK_TRACE : DJVM.copyToDJVM(elements, startIdx, endIdx);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static boolean isConstructorFor(java.lang.StackTraceElement elt, java.lang.String className) {
|
||||
return elt.getClassName().equals(className) && elt.getMethodName().equals("<init>");
|
||||
}
|
||||
|
||||
public void printStackTrace() {}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
java.lang.Throwable fromDJVM() {
|
||||
return DJVM.fromDJVM(this);
|
||||
}
|
||||
}
|
18
djvm/src/main/java/sandbox/java/nio/charset/Charset.java
Normal file
18
djvm/src/main/java/sandbox/java/nio/charset/Charset.java
Normal file
@ -0,0 +1,18 @@
|
||||
package sandbox.java.nio.charset;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.nio.charset.Charset}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Charset extends sandbox.java.lang.Object {
|
||||
private final sandbox.java.lang.String canonicalName;
|
||||
|
||||
protected Charset(sandbox.java.lang.String canonicalName, sandbox.java.lang.String[] aliases) {
|
||||
this.canonicalName = canonicalName;
|
||||
}
|
||||
|
||||
public final sandbox.java.lang.String name() {
|
||||
return canonicalName;
|
||||
}
|
||||
}
|
9
djvm/src/main/java/sandbox/java/util/Comparator.java
Normal file
9
djvm/src/main/java/sandbox/java/util/Comparator.java
Normal file
@ -0,0 +1,9 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.util.Comparator}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Comparator<T> extends java.util.Comparator<T> {
|
||||
}
|
13
djvm/src/main/java/sandbox/java/util/LinkedHashMap.java
Normal file
13
djvm/src/main/java/sandbox/java/util/LinkedHashMap.java
Normal file
@ -0,0 +1,13 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class to bootstrap us into the sandbox.
|
||||
*/
|
||||
public class LinkedHashMap<K, V> extends java.util.LinkedHashMap<K, V> implements Map<K, V> {
|
||||
public LinkedHashMap(int initialSize) {
|
||||
super(initialSize);
|
||||
}
|
||||
|
||||
public LinkedHashMap() {
|
||||
}
|
||||
}
|
9
djvm/src/main/java/sandbox/java/util/Locale.java
Normal file
9
djvm/src/main/java/sandbox/java/util/Locale.java
Normal file
@ -0,0 +1,9 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.util.Locale}
|
||||
* to allow us to compile {@link sandbox.java.lang.String}.
|
||||
*/
|
||||
public abstract class Locale extends sandbox.java.lang.Object {
|
||||
public abstract sandbox.java.lang.String toLanguageTag();
|
||||
}
|
7
djvm/src/main/java/sandbox/java/util/Map.java
Normal file
7
djvm/src/main/java/sandbox/java/util/Map.java
Normal file
@ -0,0 +1,7 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class to bootstrap us into the sandbox.
|
||||
*/
|
||||
public interface Map<K, V> extends java.util.Map<K, V> {
|
||||
}
|
10
djvm/src/main/java/sandbox/java/util/function/Function.java
Normal file
10
djvm/src/main/java/sandbox/java/util/function/Function.java
Normal file
@ -0,0 +1,10 @@
|
||||
package sandbox.java.util.function;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.util.function.Function}
|
||||
* to allow us to compile {@link sandbox.Task}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Function<T, R> {
|
||||
R apply(T item);
|
||||
}
|
10
djvm/src/main/java/sandbox/java/util/function/Supplier.java
Normal file
10
djvm/src/main/java/sandbox/java/util/function/Supplier.java
Normal file
@ -0,0 +1,10 @@
|
||||
package sandbox.java.util.function;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of @{link java.util.function.Supplier}
|
||||
* to allow us to compile {@link sandbox.java.lang.ThreadLocal}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Supplier<T> {
|
||||
T get();
|
||||
}
|
10
djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java
Normal file
10
djvm/src/main/java/sandbox/sun/misc/JavaLangAccess.java
Normal file
@ -0,0 +1,10 @@
|
||||
package sandbox.sun.misc;
|
||||
|
||||
import sandbox.java.lang.Enum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface JavaLangAccess {
|
||||
|
||||
<E extends Enum<E>> E[] getEnumConstantsShared(Class<E> enumClass);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user