Skip to content

Commit

Permalink
CORDA-3750: Reimplement Corda's Crypto object for use inside the sand…
Browse files Browse the repository at this point in the history
…box. (corda#6193)

* CORDA-3750: Use hand-written sandbox Crypto object that delegates to the node.

* CORDA-3750: Add integration test for deterministic CashIssueAndPayment flow.

* Tidy up generics for Array instances.

* Upgrade to DJVM 1.1-RC04.
  • Loading branch information
chrisr3 authored May 26, 2020
1 parent 4ed5750 commit 6ebc6e9
Show file tree
Hide file tree
Showing 30 changed files with 694 additions and 13 deletions.
2 changes: 1 addition & 1 deletion constants.properties
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ snakeYamlVersion=1.19
caffeineVersion=2.7.0
metricsVersion=4.1.0
metricsNewRelicVersion=1.1.1
djvmVersion=1.1-RC03
djvmVersion=1.1-RC04
deterministicRtVersion=1.0-RC02
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
Expand Down
5 changes: 5 additions & 0 deletions node/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ quasar {

jar {
baseName 'corda-node'
exclude 'sandbox/java/**'
exclude 'sandbox/org/**'
exclude 'sandbox/net/corda/core/crypto/SecureHash.class'
exclude 'sandbox/net/corda/core/crypto/SignatureScheme.class'
exclude 'sandbox/net/corda/core/crypto/TransactionSignature.class'
manifest {
attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name)
attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' '))
Expand Down
8 changes: 4 additions & 4 deletions node/djvm/src/main/kotlin/net/corda/node/djvm/LtxFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ private const val TX_PRIVACY_SALT = 7
private const val TX_NETWORK_PARAMETERS = 8
private const val TX_REFERENCES = 9

class LtxFactory : Function<Array<Any?>, LedgerTransaction> {
class LtxFactory : Function<Array<out Any?>, LedgerTransaction> {

@Suppress("unchecked_cast")
override fun apply(txArgs: Array<Any?>): LedgerTransaction {
override fun apply(txArgs: Array<out Any?>): LedgerTransaction {
return LedgerTransaction.createForSandbox(
inputs = (txArgs[TX_INPUTS] as Array<Array<Any?>>).map { it.toStateAndRef() },
inputs = (txArgs[TX_INPUTS] as Array<Array<out Any?>>).map { it.toStateAndRef() },
outputs = (txArgs[TX_OUTPUTS] as? List<TransactionState<ContractState>>) ?: emptyList(),
commands = (txArgs[TX_COMMANDS] as? List<CommandWithParties<CommandData>>) ?: emptyList(),
attachments = (txArgs[TX_ATTACHMENTS] as? List<Attachment>) ?: emptyList(),
Expand All @@ -41,7 +41,7 @@ class LtxFactory : Function<Array<Any?>, LedgerTransaction> {
timeWindow = txArgs[TX_TIME_WINDOW] as? TimeWindow,
privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt,
networkParameters = txArgs[TX_NETWORK_PARAMETERS] as NetworkParameters,
references = (txArgs[TX_REFERENCES] as Array<Array<Any?>>).map { it.toStateAndRef() }
references = (txArgs[TX_REFERENCES] as Array<Array<out Any?>>).map { it.toStateAndRef() }
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package net.corda.node.services

import net.corda.core.messaging.startFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.node.DeterministicSourcesRule
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.NotarySpec
import net.corda.testing.node.internal.findCordapp
import org.junit.ClassRule
import org.junit.Test
import org.junit.jupiter.api.assertDoesNotThrow

@Suppress("FunctionName")
class DeterministicCashIssueAndPaymentTest {
companion object {
val logger = loggerFor<DeterministicCashIssueAndPaymentTest>()

@ClassRule
@JvmField
val djvmSources = DeterministicSourcesRule()

@JvmField
val CASH_AMOUNT = 500.DOLLARS

fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
return DriverParameters(
portAllocation = incrementalPortAllocation(),
startNodesInProcess = false,
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
cordappsForAllNodes = listOf(
findCordapp("net.corda.finance.contracts"),
findCordapp("net.corda.finance.workflows")
),
djvmBootstrapSource = djvmSources.bootstrap,
djvmCordaSource = djvmSources.corda
)
}
}

@Test(timeout = 300_000)
fun `test DJVM can issue cash`() {
val reference = OpaqueBytes.of(0x01)
driver(parametersFor(djvmSources)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val aliceParty = alice.nodeInfo.singleIdentity()
val notaryParty = notaryHandles.single().identity
val txId = assertDoesNotThrow {
alice.rpc.startFlow(::CashIssueAndPaymentFlow,
CASH_AMOUNT,
reference,
aliceParty,
false,
notaryParty
).returnValue.getOrThrow()
}
logger.info("TX-ID: {}", txId)
}
}
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/lang/CharSequence.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.lang;

/**
* This is a dummy class that implements just enough of {@link java.lang.CharSequence}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface CharSequence extends java.lang.CharSequence {
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/lang/Comparable.java
Original file line number Diff line number Diff line change
@@ -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.net.corda.core.crypto.Crypto}.
*/
public interface Comparable<T> extends java.lang.Comparable<T> {
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/lang/Number.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.lang;

/**
* This is a dummy class that implements just enough of {@link java.lang.Number}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public class Number extends Object {
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/math/BigInteger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.math;

/**
* This is a dummy class that implements just enough of {@link java.math.BigInteger}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public class BigInteger extends sandbox.java.lang.Number {
}
13 changes: 13 additions & 0 deletions node/src/main/java/sandbox/java/security/Key.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sandbox.java.security;

import sandbox.java.lang.String;

/**
* This is a dummy class that implements just enough of {@link java.security.Key}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface Key {
String getAlgorithm();
String getFormat();
byte[] getEncoded();
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/security/KeyPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.security;

/**
* This is a dummy class that implements just enough of {@link java.security.KeyPair}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public final class KeyPair extends sandbox.java.lang.Object implements java.io.Serializable {
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/security/PrivateKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.security;

/**
* This is a dummy class that implements just enough of {@link java.security.PrivateKey}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface PrivateKey extends Key {
}
8 changes: 8 additions & 0 deletions node/src/main/java/sandbox/java/security/PublicKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.security;

/**
* This is a dummy class that implements just enough of {@link java.security.PublicKey}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface PublicKey extends Key {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sandbox.java.security.spec;

/**
* This is a dummy class that implements just enough of {@link java.security.spec.AlgorithmParameterSpec}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface AlgorithmParameterSpec {
}
16 changes: 16 additions & 0 deletions node/src/main/java/sandbox/java/util/ArrayList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package sandbox.java.util;

/**
* This is a dummy class that implements just enough of {@link java.util.ArrayList}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
@SuppressWarnings("unused")
public class ArrayList<T> extends sandbox.java.lang.Object implements List<T> {
public ArrayList(int size) {
}

@Override
public boolean add(T item) {
throw new UnsupportedOperationException("Dummy class - not implemented");
}
}
9 changes: 9 additions & 0 deletions node/src/main/java/sandbox/java/util/List.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sandbox.java.util;

/**
* This is a dummy class that implements just enough of {@link java.util.List}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
public interface List<T> {
boolean add(T item);
}
62 changes: 62 additions & 0 deletions node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package sandbox.net.corda.core.crypto;

import org.jetbrains.annotations.NotNull;
import sandbox.java.lang.Integer;
import sandbox.java.lang.String;
import sandbox.java.util.ArrayList;
import sandbox.java.util.List;
import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier;

import java.io.IOException;

/**
* Helper class for {@link sandbox.net.corda.core.crypto.Crypto}.
* Deliberately package-private.
*/
final class DJVM {
private DJVM() {}

@NotNull
static SignatureScheme toDJVM(@NotNull net.corda.core.crypto.SignatureScheme scheme) {
// The AlgorithmParameterSpec is deliberately left as null
// because it is computationally expensive to generate these
// objects inside the sandbox every time.
return new SignatureScheme(
scheme.getSchemeNumberID(),
String.toDJVM(scheme.getSchemeCodeName()),
toDJVM(scheme.getSignatureOID()),
toDJVM(scheme.getAlternativeOIDs()),
String.toDJVM(scheme.getProviderName()),
String.toDJVM(scheme.getAlgorithmName()),
String.toDJVM(scheme.getSignatureName()),
null,
Integer.toDJVM(scheme.getKeySize()),
String.toDJVM(scheme.getDesc())
);
}

static org.bouncycastle.asn1.x509.AlgorithmIdentifier fromDJVM(@NotNull AlgorithmIdentifier oid) {
try {
return org.bouncycastle.asn1.x509.AlgorithmIdentifier.getInstance(oid.getEncoded());
} catch (IOException e) {
throw sandbox.java.lang.DJVM.toRuleViolationError(e);
}
}

static AlgorithmIdentifier toDJVM(@NotNull org.bouncycastle.asn1.x509.AlgorithmIdentifier oid) {
try {
return AlgorithmIdentifier.getInstance(oid.getEncoded());
} catch (IOException e) {
throw sandbox.java.lang.DJVM.toRuleViolationError(e);
}
}

@NotNull
static List<AlgorithmIdentifier> toDJVM(@NotNull java.util.List<org.bouncycastle.asn1.x509.AlgorithmIdentifier> list) {
ArrayList<AlgorithmIdentifier> djvmList = new ArrayList<>(list.size());
for (org.bouncycastle.asn1.x509.AlgorithmIdentifier oid : list) {
djvmList.add(toDJVM(oid));
}
return djvmList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package sandbox.net.corda.core.crypto;

import org.jetbrains.annotations.NotNull;
import sandbox.java.lang.Object;
import sandbox.java.lang.String;
import sandbox.java.security.PublicKey;

/**
* We shall delegate as much cryptography as possible to Corda's
* underlying {@link net.corda.core.crypto.Crypto} object and its
* {@link java.security.Provider} classes. This wrapper only needs
* to implement {@link #equals} and {@link #hashCode}.
*/
final class DJVMPublicKey extends Object implements PublicKey {
private final java.security.PublicKey underlying;
private final String algorithm;
private final String format;
private final int hashCode;

DJVMPublicKey(@NotNull java.security.PublicKey underlying) {
this.underlying = underlying;
this.algorithm = String.toDJVM(underlying.getAlgorithm());
this.format = String.toDJVM(underlying.getFormat());
this.hashCode = underlying.hashCode();
}

java.security.PublicKey getUnderlying() {
return underlying;
}

@Override
public boolean equals(java.lang.Object other) {
if (this == other) {
return true;
} else if (!(other instanceof DJVMPublicKey)) {
return false;
} else {
return underlying.equals(((DJVMPublicKey) other).underlying);
}
}

@Override
public int hashCode() {
return hashCode;
}

@Override
public String getAlgorithm() {
return algorithm;
}

@Override
public String getFormat() {
return format;
}

@Override
public byte[] getEncoded() {
return underlying.getEncoded();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sandbox.org.bouncycastle.asn1;

/**
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Encodable}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
@SuppressWarnings("WeakerAccess")
public interface ASN1Encodable {
}
16 changes: 16 additions & 0 deletions node/src/main/java/sandbox/org/bouncycastle/asn1/ASN1Object.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package sandbox.org.bouncycastle.asn1;

import sandbox.java.lang.Object;

import java.io.IOException;

/**
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Object}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
@SuppressWarnings("RedundantThrows")
public class ASN1Object extends Object implements ASN1Encodable {
public byte[] getEncoded() throws IOException {
throw new UnsupportedOperationException("Dummy class - not implemented");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package sandbox.org.bouncycastle.asn1.x509;

import sandbox.org.bouncycastle.asn1.ASN1Object;

/**
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier}
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
*/
@SuppressWarnings("unused")
public class AlgorithmIdentifier extends ASN1Object {
public static AlgorithmIdentifier getInstance(Object obj) {
throw new UnsupportedOperationException("Dummy class - not implemented");
}
}
Loading

0 comments on commit 6ebc6e9

Please sign in to comment.