ENT-12248 Support for a new legacy-jars directory of 3rd party JARs for the external verifier

This commit is contained in:
rick.parker 2024-10-08 10:09:39 +01:00
parent babaceab5d
commit 38d7d71a63
9 changed files with 414 additions and 5 deletions

View File

@ -54,7 +54,15 @@ processSmokeTestResources {
rename 'corda-finance-contracts-.*.jar', 'corda-finance-contracts.jar'
}
from(tasks.getByPath(":testing:cordapps:4.11-workflows:jar"))
from(configurations.corda4_11)
from(configurations.corda4_11) {
rename 'jackson-core-.*.jar', 'jackson-core.jar'
}
from(tasks.getByPath(":legacy411:jar")) {
rename 'legacy411-.*.jar', 'legacy411.jar'
}
from(tasks.getByPath(":legacy412:jar")) {
rename 'legacy412-.*.jar', 'legacy412.jar'
}
}
processIntegrationTestResources {
@ -123,6 +131,8 @@ dependencies {
smokeTestImplementation project(":finance:workflows")
smokeTestImplementation project(":testing:cordapps:4.11-workflows")
smokeTestImplementation project(":finance:contracts")
smokeTestImplementation project(":legacy411")
smokeTestImplementation project(":legacy412")
smokeTestImplementation "org.assertj:assertj-core:${assertj_version}"
smokeTestImplementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
smokeTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
@ -137,6 +147,7 @@ dependencies {
corda4_11 "net.corda:corda-finance-contracts:4.11"
corda4_11 "net.corda:corda-finance-workflows:4.11"
corda4_11 "net.corda:corda:4.11"
corda4_11 "com.fasterxml.jackson.core:jackson-core:2.17.2"
}
tasks.withType(Test).configureEach {

View File

@ -3,6 +3,7 @@ package net.corda.coretests.verification
import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.notUsed
import net.corda.core.contracts.Amount
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.CordaX500Name
@ -24,6 +25,7 @@ import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.workflows.getCashBalance
import net.corda.legacy.workflows.LegacyIssuanceFlow
import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess
@ -63,6 +65,8 @@ class ExternalVerificationSignedCordappsTest {
val (legacyContractsCordapp, legacyWorkflowsCordapp) = listOf("contracts", "workflows").map { smokeTestResource("corda-finance-$it-4.11.jar") }
// The current version finance CorDapp jars
val currentCordapps = listOf("contracts", "workflows").map { smokeTestResource("corda-finance-$it.jar") }
val legacyJacksonCordapp411 = smokeTestResource("legacy411.jar")
val legacyJacksonCordapp412 = smokeTestResource("legacy412.jar")
notaries = factory.createNotaries(
nodeParams(DUMMY_NOTARY_NAME, cordappJars = currentCordapps, legacyContractJars = listOf(legacyContractsCordapp)),
@ -76,9 +80,15 @@ class ExternalVerificationSignedCordappsTest {
))
currentNode = factory.createNode(nodeParams(
CordaX500Name("New", "York", "US"),
currentCordapps,
listOf(legacyContractsCordapp)
currentCordapps + listOf(legacyJacksonCordapp412),
listOf(legacyContractsCordapp, legacyJacksonCordapp411)
))
val legacyJars = currentNode.nodeDir / "legacy-jars"
legacyJars.toFile().mkdir()
val jacksonDestination = legacyJars / "jackson-core.jar"
val jacksonSource = smokeTestResource("jackson-core.jar")
jacksonSource.copyTo(jacksonDestination)
}
@AfterClass
@ -123,6 +133,18 @@ class ExternalVerificationSignedCordappsTest {
oldRpc.startFlow(::IssueAndChangeNotaryFlow, notaryIdentities[0], notaryIdentities[1]).returnValue.getOrThrow()
}
@Test(timeout = 300_000)
fun `transaction containing 4_11 and 4_12 contract referencing Jackson dependency issued on new node`() {
val issuanceStateRef = legacyJackonIssuance(currentNode)
currentNode.assertTransactionsWereVerified(BOTH, issuanceStateRef.txhash)
}
private fun legacyJackonIssuance(issuer: NodeProcess): StateRef {
val issuerRpc = issuer.connect(superUser).proxy
val issuanceStateRef = issuerRpc.startFlowDynamic(LegacyIssuanceFlow::class.java, 2).returnValue.getOrThrow() as StateRef
return issuanceStateRef
}
private fun cashIssuanceAndPayment(issuer: NodeProcess, recipient: NodeProcess): Pair<SignedTransaction, SignedTransaction> {
val issuerRpc = issuer.connect(superUser).proxy
val recipientRpc = recipient.connect(superUser).proxy

39
legacy411/build.gradle Normal file
View File

@ -0,0 +1,39 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'java'
compileJava {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
description 'Legacy CorDapp for testing'
dependencies {
cordaProvided("net.corda:corda-core:4.11") {
exclude group: quasar_group, module: 'quasar-core'
}
cordaProvided configurations['quasar']
cordaProvided "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
}
cordapp {
targetPlatformVersion 1
minimumPlatformVersion 1
sealing {
enabled false // This needs to be disabled for AttachmentsClassLoaderSerializationTests to work
}
contract {
name "Legacy Test CorDapp"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
workflow {
name "Legacy Test CorDapp"
versionId 1
vendor "R3"
licence "Open Source (Apache 2)"
}
}

View File

@ -0,0 +1,116 @@
package net.corda.legacy.contracts;
import com.fasterxml.jackson.core.JsonProcessingException;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.internal.Intrinsics;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
import net.corda.core.contracts.ContractState;
import net.corda.core.contracts.PartyAndReference;
import net.corda.core.contracts.StateAndContract;
import net.corda.core.contracts.TypeOnlyCommandData;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.transactions.TransactionBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public final class AnotherDummyContract implements Contract {
@NotNull
private final String magicString = "helloworld";
@NotNull
public static final String ANOTHER_DUMMY_PROGRAM_ID = "net.corda.legacy.contracts.AnotherDummyContract";
@NotNull
public final String getMagicString() {
return this.magicString;
}
public void verify(@NotNull LedgerTransaction tx) {
Intrinsics.checkNotNullParameter(tx, "tx");
}
public void randomMethod() throws JsonProcessingException {
throw new JsonProcessingException("") {
};
}
@NotNull
public final TransactionBuilder generateInitial(@NotNull PartyAndReference owner, int magicNumber, @NotNull Party notary) {
Intrinsics.checkNotNullParameter(owner, "owner");
Intrinsics.checkNotNullParameter(notary, "notary");
State state = new State(magicNumber);
TransactionBuilder var10000 = new TransactionBuilder(notary);
Object[] var5 = new Object[]{new StateAndContract((ContractState) state, ANOTHER_DUMMY_PROGRAM_ID), new Command<Commands.Create>(new Commands.Create(), owner.getParty().getOwningKey())};
return var10000.withItems(var5);
}
public final int inspectState(@NotNull ContractState state) {
Intrinsics.checkNotNullParameter(state, "state");
return ((State) state).getMagicNumber();
}
public interface Commands extends CommandData {
public static final class Create extends TypeOnlyCommandData implements Commands {
}
}
public static final class State implements ContractState {
private final int magicNumber;
public State(int magicNumber) {
this.magicNumber = magicNumber;
}
public final int getMagicNumber() {
return this.magicNumber;
}
@NotNull
public List<AbstractParty> getParticipants() {
return CollectionsKt.emptyList();
}
public final int component1() {
return this.magicNumber;
}
@NotNull
public final State copy(int magicNumber) {
return new State(magicNumber);
}
// $FF: synthetic method
public static State copy$default(State var0, int var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.magicNumber;
}
return var0.copy(var1);
}
@NotNull
public String toString() {
return "State(magicNumber=" + this.magicNumber + ')';
}
public int hashCode() {
return Integer.hashCode(this.magicNumber);
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (!(other instanceof State)) {
return false;
} else {
State var2 = (State) other;
return this.magicNumber == var2.magicNumber;
}
}
}
}

39
legacy412/build.gradle Normal file
View File

@ -0,0 +1,39 @@
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'java'
compileJava {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
description 'Legacy CorDapp for testing'
dependencies {
cordaProvided("net.corda:corda-core:4.11") {
exclude group: quasar_group, module: 'quasar-core'
}
cordaProvided configurations['quasar']
cordaProvided "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
}
cordapp {
targetPlatformVersion 1
minimumPlatformVersion 1
sealing {
enabled false // This needs to be disabled for AttachmentsClassLoaderSerializationTests to work
}
contract {
name "Legacy Test CorDapp"
versionId 2
vendor "R3"
licence "Open Source (Apache 2)"
}
workflow {
name "Legacy Test CorDapp"
versionId 2
vendor "R3"
licence "Open Source (Apache 2)"
}
}

View File

@ -0,0 +1,116 @@
package net.corda.legacy.contracts;
import com.fasterxml.jackson.core.JsonProcessingException;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.internal.Intrinsics;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
import net.corda.core.contracts.ContractState;
import net.corda.core.contracts.PartyAndReference;
import net.corda.core.contracts.StateAndContract;
import net.corda.core.contracts.TypeOnlyCommandData;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.transactions.TransactionBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public final class AnotherDummyContract implements Contract {
@NotNull
private final String magicString = "helloworld";
@NotNull
public static final String ANOTHER_DUMMY_PROGRAM_ID = "net.corda.legacy.contracts.AnotherDummyContract";
@NotNull
public final String getMagicString() {
return this.magicString;
}
public void verify(@NotNull LedgerTransaction tx) {
Intrinsics.checkNotNullParameter(tx, "tx");
}
public void randomMethod() throws JsonProcessingException {
throw new JsonProcessingException("") {
};
}
@NotNull
public final TransactionBuilder generateInitial(@NotNull PartyAndReference owner, int magicNumber, @NotNull Party notary) {
Intrinsics.checkNotNullParameter(owner, "owner");
Intrinsics.checkNotNullParameter(notary, "notary");
State state = new State(magicNumber);
TransactionBuilder var10000 = new TransactionBuilder(notary);
Object[] var5 = new Object[]{new StateAndContract((ContractState) state, ANOTHER_DUMMY_PROGRAM_ID), new Command<Commands.Create>(new Commands.Create(), owner.getParty().getOwningKey())};
return var10000.withItems(var5);
}
public final int inspectState(@NotNull ContractState state) {
Intrinsics.checkNotNullParameter(state, "state");
return ((State) state).getMagicNumber();
}
public interface Commands extends CommandData {
public static final class Create extends TypeOnlyCommandData implements Commands {
}
}
public static final class State implements ContractState {
private final int magicNumber;
public State(int magicNumber) {
this.magicNumber = magicNumber;
}
public final int getMagicNumber() {
return this.magicNumber;
}
@NotNull
public List<AbstractParty> getParticipants() {
return CollectionsKt.emptyList();
}
public final int component1() {
return this.magicNumber;
}
@NotNull
public final State copy(int magicNumber) {
return new State(magicNumber);
}
// $FF: synthetic method
public static State copy$default(State var0, int var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.magicNumber;
}
return var0.copy(var1);
}
@NotNull
public String toString() {
return "State(magicNumber=" + this.magicNumber + ')';
}
public int hashCode() {
return Integer.hashCode(this.magicNumber);
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (!(other instanceof State)) {
return false;
} else {
State var2 = (State) other;
return this.magicNumber == var2.magicNumber;
}
}
}
}

View File

@ -0,0 +1,54 @@
package net.corda.legacy.workflows;
import co.paralleluniverse.fibers.Suspendable;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.internal.Intrinsics;
import net.corda.core.contracts.AttachmentResolutionException;
import net.corda.core.contracts.StateRef;
import net.corda.core.contracts.TransactionResolutionException;
import net.corda.core.contracts.TransactionVerificationException;
import net.corda.core.flows.FlowLogic;
import net.corda.core.flows.StartableByRPC;
import net.corda.core.identity.Party;
import net.corda.core.node.ServiceHub;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.legacy.contracts.AnotherDummyContract;
import org.jetbrains.annotations.NotNull;
import java.security.SignatureException;
@StartableByRPC
public final class LegacyIssuanceFlow extends FlowLogic {
private final int magicNumber;
public LegacyIssuanceFlow(int magicNumber) {
this.magicNumber = magicNumber;
}
@Suspendable
@NotNull
public StateRef call() {
ServiceHub var10000 = this.getServiceHub();
AnotherDummyContract var10001 = new AnotherDummyContract();
Party var10002 = this.getOurIdentity();
byte[] var3 = new byte[]{0};
TransactionBuilder var2 = var10001.generateInitial(var10002.ref(var3), this.magicNumber, (Party) CollectionsKt.first(this.getServiceHub().getNetworkMapCache().getNotaryIdentities()));
Intrinsics.checkNotNullExpressionValue(var2, "generateInitial(...)");
SignedTransaction stx = var10000.signInitialTransaction(var2);
try {
stx.verify(this.getServiceHub(), false);
} catch (SignatureException e) {
throw new RuntimeException(e);
} catch (AttachmentResolutionException e) {
throw new RuntimeException(e);
} catch (TransactionResolutionException e) {
throw new RuntimeException(e);
} catch (TransactionVerificationException e) {
throw new RuntimeException(e);
}
//SignedTransaction.verify$default(stx, this.getServiceHub(), false, 2, (Object)null);
this.getServiceHub().recordTransactions(stx, new SignedTransaction[0]);
return stx.getTx().outRef(0).getRef();
}
}

View File

@ -36,6 +36,7 @@ import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.Verifi
import net.corda.serialization.internal.verifier.ExternalVerifierOutbound.VerifierRequest.GetTrustedClassAttachments
import net.corda.serialization.internal.verifier.readCordaSerializable
import net.corda.serialization.internal.verifier.writeCordaSerializable
import java.io.File
import java.io.IOException
import java.lang.Character.MAX_RADIX
import java.lang.ProcessBuilder.Redirect
@ -206,9 +207,18 @@ class ExternalVerifierHandleImpl(
val command = ArrayList<String>()
command += "${Path(System.getProperty("java.home"), "bin", "java")}"
command += inheritedJvmArgs
// Build list of 3rd party jars
val legacyJarsPath = baseDirectory / "legacy-jars"
val extraClassPath = legacyJarsPath.toFile().listFiles { _, name ->
name.endsWith(".jar")
}?.joinToString(File.pathSeparator, File.pathSeparator)
val classpath = if (extraClassPath == null) "$verifierJar" else "$verifierJar$extraClassPath"
command += listOf(
"-jar",
"$verifierJar",
"-cp",
classpath,
"net.corda.verifier.Main",
socketFile.absolutePathString(),
log.level.name.lowercase()
)

View File

@ -44,6 +44,8 @@ include 'finance:workflows'
include 'core'
include 'core-1.2'
include 'core-tests'
include 'legacy411'
include 'legacy412'
include 'docs'
include 'node-api'
include 'node-api-tests'