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:
tudor.malene@gmail.com 2018-10-24 17:09:30 +01:00
commit 29a8c153ed
532 changed files with 13655 additions and 5037 deletions

View File

@ -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
View File

@ -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" />

View File

@ -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)

View File

@ -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'

View File

@ -9,6 +9,7 @@ buildCache {
enabled = !isCiServer
}
remote(HttpBuildCache) {
enabled = isCiServer
url = gradleBuildCacheURL
push = isCiServer
}

View File

@ -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" }

View File

@ -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")

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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()))
},

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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(

View File

@ -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"

View File

@ -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

View File

@ -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>
/**

View File

@ -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

View File

@ -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
}

View File

@ -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>?
}

View File

@ -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())
}
}

View File

@ -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)
/**

View File

@ -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

View File

@ -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)

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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) }

View File

@ -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 }
}

View File

@ -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)
}

View File

@ -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()
}
}
}

View File

@ -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 }
}
}
}

View File

@ -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
/**

View File

@ -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]
*/

View File

@ -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 {

View File

@ -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

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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.

View File

@ -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)
}
}
}

View File

@ -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");
}
}

View File

@ -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))
}
}

View File

@ -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")
}
}
}

View File

@ -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 }
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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!!)
}
}
}
}

View File

@ -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())
}
}
}

View File

@ -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)

View File

@ -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))
}
}
}

View File

@ -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.
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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 }
}

View File

@ -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 {

View File

@ -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.
}

View File

@ -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

View File

@ -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) }
}

View 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;
}

View 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); }
}

View 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);
}
}

View 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();
}

View 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() {}
}
}

View 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> {
}

View File

@ -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;
}
}

View 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);
}
}

View 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");
}
}

View 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);
}
}

View 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);
}
}
}

View 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();
}

View 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);
}
}
}

View 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());
}
}

View 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());
}
}

View 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() {}
}

View 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);
}
}

View 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 + ')' : "")
);
}
}

View 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();
}
}

View 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);
}

View 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);
}

View 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() {}
}

View 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();
}
}
}

View 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);
}
}

View 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;
}
}

View 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> {
}

View 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() {
}
}

View 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();
}

View 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> {
}

View 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);
}

View 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();
}

View 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