diff --git a/.ci/api-current.txt b/.ci/api-current.txt index c44ad203da..0028c8444c 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -1,7 +1,4 @@ @CordaSerializable -public interface net.corda.core.ClientRelevantError -## -@CordaSerializable public class net.corda.core.CordaException extends java.lang.Exception implements net.corda.core.CordaThrowable public () public (String) @@ -67,12 +64,6 @@ public interface net.corda.core.CordaThrowable public @interface net.corda.core.DoNotImplement ## public final class net.corda.core.Utils extends java.lang.Object - @NotNull - public static final net.corda.core.messaging.DataFeed doOnError(net.corda.core.messaging.DataFeed, kotlin.jvm.functions.Function1) - @NotNull - public static final net.corda.core.messaging.DataFeed mapErrors(net.corda.core.messaging.DataFeed, kotlin.jvm.functions.Function1) - @NotNull - public static final rx.Observable mapErrors(rx.Observable, kotlin.jvm.functions.Function1) @NotNull public static final net.corda.core.concurrent.CordaFuture toFuture(rx.Observable) @NotNull @@ -87,7 +78,6 @@ public final class net.corda.core.concurrent.ConcurrencyUtils extends java.lang. @NotNull public static final String shortCircuitedTaskFailedMessage = "Short-circuited task failed:" ## -@CordaSerializable public interface net.corda.core.concurrent.CordaFuture extends java.util.concurrent.Future public abstract void then(kotlin.jvm.functions.Function1, ? extends W>) @NotNull @@ -114,7 +104,6 @@ public final class net.corda.core.context.Actor extends java.lang.Object public int hashCode() @NotNull public static final net.corda.core.context.Actor service(String, net.corda.core.identity.CordaX500Name) - @NotNull public String toString() public static final net.corda.core.context.Actor$Companion Companion ## @@ -133,7 +122,6 @@ public static final class net.corda.core.context.Actor$Id extends java.lang.Obje @NotNull public final String getValue() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -147,7 +135,6 @@ public final class net.corda.core.context.AuthServiceId extends java.lang.Object @NotNull public final String getValue() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -191,7 +178,6 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob public static final net.corda.core.context.InvocationContext service(String, net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace) @NotNull public static final net.corda.core.context.InvocationContext shell(net.corda.core.context.Trace, net.corda.core.context.Trace) - @NotNull public String toString() public static final net.corda.core.context.InvocationContext$Companion Companion ## @@ -227,23 +213,17 @@ public static final class net.corda.core.context.InvocationOrigin$Peer extends n public int hashCode() @NotNull public java.security.Principal principal() - @NotNull public String toString() ## @CordaSerializable public static final class net.corda.core.context.InvocationOrigin$RPC extends net.corda.core.context.InvocationOrigin public (net.corda.core.context.Actor) @NotNull - public final net.corda.core.context.Actor component1() - @NotNull public final net.corda.core.context.InvocationOrigin$RPC copy(net.corda.core.context.Actor) public boolean equals(Object) - @NotNull - public final net.corda.core.context.Actor getActor() public int hashCode() @NotNull public java.security.Principal principal() - @NotNull public String toString() ## @CordaSerializable @@ -259,7 +239,6 @@ public static final class net.corda.core.context.InvocationOrigin$Scheduled exte public int hashCode() @NotNull public java.security.Principal principal() - @NotNull public String toString() ## @CordaSerializable @@ -279,7 +258,6 @@ public static final class net.corda.core.context.InvocationOrigin$Service extend public int hashCode() @NotNull public java.security.Principal principal() - @NotNull public String toString() ## @CordaSerializable @@ -305,7 +283,6 @@ public final class net.corda.core.context.Trace extends java.lang.Object public int hashCode() @NotNull public static final net.corda.core.context.Trace newInstance(net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId) - @NotNull public String toString() public static final net.corda.core.context.Trace$Companion Companion ## @@ -338,7 +315,6 @@ public static final class net.corda.core.context.Trace$SessionId$Companion exten @DoNotImplement @CordaSerializable public final class net.corda.core.contracts.AlwaysAcceptAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public static final net.corda.core.contracts.AlwaysAcceptAttachmentConstraint INSTANCE ## @@ -465,14 +441,8 @@ public interface net.corda.core.contracts.Attachment extends net.corda.core.cont @DoNotImplement @CordaSerializable public interface net.corda.core.contracts.AttachmentConstraint - public abstract boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) public abstract boolean isSatisfiedBy(net.corda.core.contracts.Attachment) ## -public final class net.corda.core.contracts.AttachmentConstraintKt extends java.lang.Object - public static final void checkConstraintValidity(net.corda.core.contracts.TransactionState) - public static final boolean contractHasAutomaticConstraintPropagation(String, ClassLoader) - public static final void warnContractWithoutConstraintPropagation(String, ClassLoader) -## @CordaSerializable public final class net.corda.core.contracts.AttachmentResolutionException extends net.corda.core.flows.FlowException public (net.corda.core.crypto.SecureHash) @@ -482,24 +452,12 @@ public final class net.corda.core.contracts.AttachmentResolutionException extend @DoNotImplement @CordaSerializable public final class net.corda.core.contracts.AutomaticHashConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public static final net.corda.core.contracts.AutomaticHashConstraint INSTANCE ## -@DoNotImplement -@CordaSerializable -public final class net.corda.core.contracts.AutomaticPlaceholderConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) - public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) - public static final net.corda.core.contracts.AutomaticPlaceholderConstraint INSTANCE -## public @interface net.corda.core.contracts.BelongsToContract public abstract Class value() ## -public final class net.corda.core.contracts.BelongsToContractKt extends java.lang.Object - @Nullable - public static final String getRequiredContractClassName(net.corda.core.contracts.ContractState) -## @CordaSerializable public final class net.corda.core.contracts.Command extends java.lang.Object public (T, java.security.PublicKey) @@ -533,7 +491,6 @@ public final class net.corda.core.contracts.CommandAndState extends java.lang.Ob @NotNull public final net.corda.core.contracts.OwnableState getOwnableState() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -558,7 +515,6 @@ public final class net.corda.core.contracts.CommandWithParties extends java.lang @NotNull public final T getValue() public int hashCode() - @NotNull public String toString() ## public final class net.corda.core.contracts.ComponentGroupEnum extends java.lang.Enum @@ -575,7 +531,6 @@ public final class net.corda.core.contracts.ContractAttachment extends java.lang public (net.corda.core.contracts.Attachment, String) public (net.corda.core.contracts.Attachment, String, java.util.Set) public (net.corda.core.contracts.Attachment, String, java.util.Set, String) - public (net.corda.core.contracts.Attachment, String, java.util.Set, String, java.util.List) @NotNull public final java.util.Set getAdditionalContracts() @NotNull @@ -614,7 +569,6 @@ public final class net.corda.core.contracts.ContractsDSL extends java.lang.Objec 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> getAmount() - @SerializableCalculatedProperty @NotNull public abstract java.util.Collection getExitKeys() @NotNull @@ -629,7 +583,6 @@ public interface net.corda.core.contracts.FungibleState extends net.corda.core.c @CordaSerializable public final class net.corda.core.contracts.HashAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint public (net.corda.core.crypto.SecureHash) - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) @NotNull public final net.corda.core.crypto.SecureHash component1() @NotNull @@ -639,7 +592,6 @@ public final class net.corda.core.contracts.HashAttachmentConstraint extends jav public final net.corda.core.crypto.SecureHash getAttachmentId() public int hashCode() public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) - @NotNull public String toString() ## @CordaSerializable @@ -695,8 +647,6 @@ public interface net.corda.core.contracts.NamedByHash @NotNull public abstract net.corda.core.crypto.SecureHash getId() ## -public @interface net.corda.core.contracts.NoConstraintPropagation -## @CordaSerializable public interface net.corda.core.contracts.OwnableState extends net.corda.core.contracts.ContractState @NotNull @@ -727,19 +677,6 @@ public final class net.corda.core.contracts.PrivacySalt extends net.corda.core.u public () public (byte[]) ## -public final class net.corda.core.contracts.ReferencedStateAndRef extends java.lang.Object - public (net.corda.core.contracts.StateAndRef) - @NotNull - public final net.corda.core.contracts.StateAndRef component1() - @NotNull - public final net.corda.core.contracts.ReferencedStateAndRef copy(net.corda.core.contracts.StateAndRef) - public boolean equals(Object) - @NotNull - public final net.corda.core.contracts.StateAndRef getStateAndRef() - public int hashCode() - @NotNull - public String toString() -## public final class net.corda.core.contracts.Requirements extends java.lang.Object public final void using(String, boolean) public static final net.corda.core.contracts.Requirements INSTANCE @@ -767,7 +704,6 @@ public final class net.corda.core.contracts.ScheduledActivity extends java.lang. @NotNull public java.time.Instant getScheduledAt() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -785,24 +721,6 @@ public final class net.corda.core.contracts.ScheduledStateRef extends java.lang. @NotNull public java.time.Instant getScheduledAt() public int hashCode() - @NotNull - public String toString() -## -@DoNotImplement -@CordaSerializable -public final class net.corda.core.contracts.SignatureAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint - public (java.security.PublicKey) - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) - @NotNull - public final java.security.PublicKey component1() - @NotNull - public final net.corda.core.contracts.SignatureAttachmentConstraint copy(java.security.PublicKey) - public boolean equals(Object) - @NotNull - public final java.security.PublicKey getKey() - public int hashCode() - public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) - @NotNull public String toString() ## public final class net.corda.core.contracts.SourceAndAmount extends java.lang.Object @@ -823,7 +741,6 @@ public final class net.corda.core.contracts.SourceAndAmount extends java.lang.Ob @NotNull public final P getSource() public int hashCode() - @NotNull public String toString() ## public final class net.corda.core.contracts.StateAndContract extends java.lang.Object @@ -840,7 +757,6 @@ public final class net.corda.core.contracts.StateAndContract extends java.lang.O @NotNull public final net.corda.core.contracts.ContractState getState() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -858,9 +774,6 @@ public final class net.corda.core.contracts.StateAndRef extends java.lang.Object @NotNull public final net.corda.core.contracts.TransactionState getState() public int hashCode() - @NotNull - public final net.corda.core.contracts.ReferencedStateAndRef referenced() - @NotNull public String toString() ## @CordaSerializable @@ -960,7 +873,6 @@ public final class net.corda.core.contracts.TransactionState extends java.lang.O public (T, String, net.corda.core.identity.Party) public (T, String, net.corda.core.identity.Party, Integer) public (T, String, net.corda.core.identity.Party, Integer, net.corda.core.contracts.AttachmentConstraint) - public (T, net.corda.core.identity.Party) @NotNull public final T component1() @NotNull @@ -985,15 +897,12 @@ public final class net.corda.core.contracts.TransactionState extends java.lang.O @NotNull public final net.corda.core.identity.Party getNotary() public int hashCode() - @NotNull public String toString() - public static final net.corda.core.contracts.TransactionState$Companion Companion ## public final class net.corda.core.contracts.TransactionStateKt extends java.lang.Object ## @CordaSerializable public abstract class net.corda.core.contracts.TransactionVerificationException extends net.corda.core.flows.FlowException - public (net.corda.core.crypto.SecureHash, String, Throwable) @NotNull public final net.corda.core.crypto.SecureHash getTxId() ## @@ -1004,20 +913,6 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept public final String getContractClass() ## @CordaSerializable -public static final class net.corda.core.contracts.TransactionVerificationException$ConstraintPropagationRejection extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, String, net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.AttachmentConstraint) - @NotNull - public final String getContractClass() -## -@CordaSerializable -public static final class net.corda.core.contracts.TransactionVerificationException$ContractAttachmentNotSignedByPackageOwnerException extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, net.corda.core.crypto.SecureHash, String) - @NotNull - public final net.corda.core.crypto.SecureHash getAttachmentHash() - @NotNull - public final String getContractClass() -## -@CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractConstraintRejection extends net.corda.core.contracts.TransactionVerificationException public (net.corda.core.crypto.SecureHash, String) @NotNull @@ -1077,10 +972,6 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept public final java.util.List getMissing() ## @CordaSerializable -public static final class net.corda.core.contracts.TransactionVerificationException$TransactionDuplicateEncumbranceException extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, int) -## -@CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$TransactionMissingEncumbranceException extends net.corda.core.contracts.TransactionVerificationException public (net.corda.core.crypto.SecureHash, int, net.corda.core.contracts.TransactionVerificationException$Direction) @NotNull @@ -1088,14 +979,6 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept public final int getMissing() ## @CordaSerializable -public static final class net.corda.core.contracts.TransactionVerificationException$TransactionNonMatchingEncumbranceException extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, java.util.Collection) -## -@CordaSerializable -public static final class net.corda.core.contracts.TransactionVerificationException$TransactionNotaryMismatchEncumbranceException extends net.corda.core.contracts.TransactionVerificationException - public (net.corda.core.crypto.SecureHash, int, int, net.corda.core.identity.Party, net.corda.core.identity.Party) -## -@CordaSerializable public abstract class net.corda.core.contracts.TypeOnlyCommandData extends java.lang.Object implements net.corda.core.contracts.CommandData public () public boolean equals(Object) @@ -1104,7 +987,6 @@ public abstract class net.corda.core.contracts.TypeOnlyCommandData extends java. @CordaSerializable public final class net.corda.core.contracts.UniqueIdentifier extends java.lang.Object implements java.lang.Comparable public () - public (String) public (String, java.util.UUID) public int compareTo(net.corda.core.contracts.UniqueIdentifier) @Nullable @@ -1142,14 +1024,11 @@ public interface net.corda.core.contracts.UpgradedContractWithLegacyConstraint e @DoNotImplement @CordaSerializable public final class net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint - public boolean canBeTransitionedFrom(net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractAttachment) public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public static final net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint INSTANCE ## @DoNotImplement public interface net.corda.core.cordapp.Cordapp - @NotNull - public abstract java.util.List>> getAllFlows() @NotNull public abstract java.util.List getContractClassNames() @NotNull @@ -1159,13 +1038,9 @@ public interface net.corda.core.cordapp.Cordapp @NotNull public abstract java.util.List>> getInitiatedFlows() @NotNull - public abstract net.corda.core.crypto.SecureHash$SHA256 getJarHash() - @NotNull public abstract java.net.URL getJarPath() @NotNull public abstract String getName() - @Nullable - public abstract Class getNotaryService() @NotNull public abstract java.util.List>> getRpcFlows() @NotNull @@ -1179,24 +1054,6 @@ public interface net.corda.core.cordapp.Cordapp @NotNull public abstract java.util.List> getServices() ## -@DoNotImplement -public interface net.corda.core.cordapp.CordappConfig - public abstract boolean exists(String) - @NotNull - public abstract Object get(String) - public abstract boolean getBoolean(String) - public abstract double getDouble(String) - public abstract float getFloat(String) - public abstract int getInt(String) - public abstract long getLong(String) - @NotNull - public abstract Number getNumber(String) - @NotNull - public abstract String getString(String) -## -public final class net.corda.core.cordapp.CordappConfigException extends java.lang.Exception - public (String, Throwable) -## public final class net.corda.core.cordapp.CordappContext extends java.lang.Object public (net.corda.core.cordapp.Cordapp, net.corda.core.crypto.SecureHash, ClassLoader, net.corda.core.cordapp.CordappConfig) @Nullable @@ -1204,8 +1061,6 @@ public final class net.corda.core.cordapp.CordappContext extends java.lang.Objec @NotNull public final ClassLoader getClassLoader() @NotNull - public final net.corda.core.cordapp.CordappConfig getConfig() - @NotNull public final net.corda.core.cordapp.Cordapp getCordapp() ## @DoNotImplement @@ -1335,7 +1190,6 @@ public static final class net.corda.core.crypto.CompositeSignature$State extends @NotNull public final net.corda.core.crypto.CompositeKey getVerifyKey() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1349,7 +1203,6 @@ public final class net.corda.core.crypto.CompositeSignaturesWithKeys extends jav @NotNull public final java.util.List getSigs() public int hashCode() - @NotNull public String toString() public static final net.corda.core.crypto.CompositeSignaturesWithKeys$Companion Companion ## @@ -1372,8 +1225,6 @@ public final class net.corda.core.crypto.CordaSecurityProvider extends java.secu ## public static final class net.corda.core.crypto.CordaSecurityProvider$Companion extends java.lang.Object ## -public final class net.corda.core.crypto.CordaSecurityProviderKt extends java.lang.Object -## public final class net.corda.core.crypto.Crypto extends java.lang.Object @NotNull public static final java.security.PrivateKey decodePrivateKey(String, byte[]) @@ -1430,7 +1281,6 @@ public final class net.corda.core.crypto.Crypto extends java.lang.Object public static final boolean isValid(net.corda.core.crypto.SecureHash, net.corda.core.crypto.TransactionSignature) public static final boolean isValid(net.corda.core.crypto.SignatureScheme, java.security.PublicKey, byte[], byte[]) public static final boolean publicKeyOnCurve(net.corda.core.crypto.SignatureScheme, java.security.PublicKey) - public static final void registerProviders() @NotNull public static final java.util.List supportedSignatureSchemes() @NotNull @@ -1439,7 +1289,6 @@ public final class net.corda.core.crypto.Crypto extends java.lang.Object public static final java.security.PublicKey toSupportedPublicKey(java.security.PublicKey) @NotNull public static final java.security.PublicKey toSupportedPublicKey(org.bouncycastle.asn1.x509.SubjectPublicKeyInfo) - public static final boolean validatePublicKey(java.security.PublicKey) @NotNull public static final net.corda.core.crypto.SignatureScheme COMPOSITE_KEY @NotNull @@ -1477,10 +1326,7 @@ public final class net.corda.core.crypto.CryptoUtils extends java.lang.Object @NotNull public static final java.security.KeyPair generateKeyPair() @NotNull - public static final java.util.Set getBlacklistedCrlEndpoints() - @NotNull public static final java.util.Set getKeys(java.security.PublicKey) - public static final boolean isCRLDistributionPointBlacklisted(java.util.List) public static final boolean isFulfilledBy(java.security.PublicKey, Iterable) public static final boolean isFulfilledBy(java.security.PublicKey, java.security.PublicKey) public static final boolean isValid(java.security.PublicKey, byte[], net.corda.core.crypto.DigitalSignature) @@ -1522,9 +1368,6 @@ public static class net.corda.core.crypto.DigitalSignature$WithKey extends net.c @NotNull public final net.corda.core.crypto.DigitalSignature withoutKey() ## -public final class net.corda.core.crypto.DummySecureRandom extends java.security.SecureRandom - public static final net.corda.core.crypto.DummySecureRandom INSTANCE -## public abstract class net.corda.core.crypto.MerkleTree extends java.lang.Object @NotNull public abstract net.corda.core.crypto.SecureHash getHash() @@ -1544,7 +1387,6 @@ public static final class net.corda.core.crypto.MerkleTree$Leaf extends net.cord @NotNull public net.corda.core.crypto.SecureHash getHash() public int hashCode() - @NotNull public String toString() ## public static final class net.corda.core.crypto.MerkleTree$Node extends net.corda.core.crypto.MerkleTree @@ -1565,7 +1407,6 @@ public static final class net.corda.core.crypto.MerkleTree$Node extends net.cord @NotNull public final net.corda.core.crypto.MerkleTree getRight() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1622,7 +1463,6 @@ public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$In @NotNull public final net.corda.core.crypto.SecureHash getHash() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1636,7 +1476,6 @@ public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$Le @NotNull public final net.corda.core.crypto.SecureHash getHash() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1654,7 +1493,6 @@ public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$No @NotNull public final net.corda.core.crypto.PartialMerkleTree$PartialTree getRight() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1676,10 +1514,6 @@ public abstract class net.corda.core.crypto.SecureHash extends net.corda.core.ut @NotNull public String toString() public static final net.corda.core.crypto.SecureHash$Companion Companion - @NotNull - public static final net.corda.core.crypto.SecureHash$SHA256 allOnesHash - @NotNull - public static final net.corda.core.crypto.SecureHash$SHA256 zeroHash ## public static final class net.corda.core.crypto.SecureHash$Companion extends java.lang.Object @NotNull @@ -1722,7 +1556,6 @@ public final class net.corda.core.crypto.SignableData extends java.lang.Object @NotNull public final net.corda.core.crypto.SecureHash getTxId() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1736,7 +1569,6 @@ public final class net.corda.core.crypto.SignatureMetadata extends java.lang.Obj public final int getPlatformVersion() public final int getSchemeNumberID() public int hashCode() - @NotNull public String toString() ## public final class net.corda.core.crypto.SignatureScheme extends java.lang.Object @@ -1783,7 +1615,6 @@ public final class net.corda.core.crypto.SignatureScheme extends java.lang.Objec @NotNull public final org.bouncycastle.asn1.x509.AlgorithmIdentifier getSignatureOID() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -1881,7 +1712,6 @@ public static final class net.corda.core.flows.AbstractStateReplacementFlow$Prop @NotNull public final net.corda.core.contracts.StateRef getStateRef() public int hashCode() - @NotNull public String toString() ## public static final class net.corda.core.flows.AbstractStateReplacementFlow$UpgradeTx extends java.lang.Object @@ -1894,7 +1724,6 @@ public static final class net.corda.core.flows.AbstractStateReplacementFlow$Upgr @NotNull public final net.corda.core.transactions.SignedTransaction getStx() public int hashCode() - @NotNull public String toString() ## @Suspendable @@ -1990,11 +1819,8 @@ public class net.corda.core.flows.DataVendingFlow extends net.corda.core.flows.F @InitiatingFlow public final class net.corda.core.flows.FinalityFlow extends net.corda.core.flows.FlowLogic public (net.corda.core.transactions.SignedTransaction) - public (net.corda.core.transactions.SignedTransaction, java.util.Collection) - public (net.corda.core.transactions.SignedTransaction, java.util.Collection, net.corda.core.utilities.ProgressTracker) public (net.corda.core.transactions.SignedTransaction, java.util.Set) public (net.corda.core.transactions.SignedTransaction, java.util.Set, net.corda.core.utilities.ProgressTracker) - public (net.corda.core.transactions.SignedTransaction, net.corda.core.flows.FlowSession, net.corda.core.flows.FlowSession...) public (net.corda.core.transactions.SignedTransaction, net.corda.core.utilities.ProgressTracker) @Suspendable @NotNull @@ -2026,13 +1852,7 @@ public class net.corda.core.flows.FlowException extends net.corda.core.CordaExce public () public (String) public (String, Throwable) - public (String, Throwable, Long) public (Throwable) - @Nullable - public Long getErrorId() - @Nullable - public final Long getOriginalErrorId() - public final void setOriginalErrorId(Long) ## @CordaSerializable public final class net.corda.core.flows.FlowInfo extends java.lang.Object @@ -2047,7 +1867,6 @@ public final class net.corda.core.flows.FlowInfo extends java.lang.Object public final String getAppName() public final int getFlowVersion() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2068,7 +1887,6 @@ public static final class net.corda.core.flows.FlowInitiator$Peer extends net.co @NotNull public final net.corda.core.identity.Party getParty() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2084,7 +1902,6 @@ public static final class net.corda.core.flows.FlowInitiator$RPC extends net.cor @NotNull public final String getUsername() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2100,7 +1917,6 @@ public static final class net.corda.core.flows.FlowInitiator$Scheduled extends n @NotNull public final net.corda.core.contracts.ScheduledStateRef getScheduledState() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2116,7 +1932,6 @@ public static final class net.corda.core.flows.FlowInitiator$Service extends net @NotNull public final String getServiceClassName() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2163,13 +1978,7 @@ public abstract class net.corda.core.flows.FlowLogic extends java.lang.Object public java.util.List> receiveAll(Class, java.util.List) @Suspendable @NotNull - public java.util.List> receiveAll(Class, java.util.List, boolean) - @Suspendable - @NotNull public java.util.Map> receiveAllMap(java.util.Map>) - @Suspendable - @NotNull - public java.util.Map> receiveAllMap(java.util.Map>, boolean) public final void recordAuditEvent(String, String, java.util.Map) @Suspendable public void send(net.corda.core.identity.Party, Object) @@ -2179,8 +1988,6 @@ public abstract class net.corda.core.flows.FlowLogic extends java.lang.Object @Suspendable public static final void sleep(java.time.Duration) @Suspendable - public static final void sleep(java.time.Duration, boolean) - @Suspendable public R subFlow(net.corda.core.flows.FlowLogic) @Nullable public final net.corda.core.messaging.DataFeed track() @@ -2194,8 +2001,6 @@ public abstract class net.corda.core.flows.FlowLogic extends java.lang.Object @Suspendable @NotNull public final net.corda.core.transactions.SignedTransaction waitForLedgerCommit(net.corda.core.crypto.SecureHash, boolean) - @Suspendable - public final void waitForStateConsumption(java.util.Set) public static final net.corda.core.flows.FlowLogic$Companion Companion ## public static final class net.corda.core.flows.FlowLogic$Companion extends java.lang.Object @@ -2203,8 +2008,6 @@ public static final class net.corda.core.flows.FlowLogic$Companion extends java. public final net.corda.core.flows.FlowLogic getCurrentTopLevel() @Suspendable public final void sleep(java.time.Duration) - @Suspendable - public final void sleep(java.time.Duration, boolean) ## @DoNotImplement @CordaSerializable @@ -2265,7 +2068,6 @@ public final class net.corda.core.flows.FlowStackSnapshot extends java.lang.Obje @NotNull public final java.time.Instant getTime() public int hashCode() - @NotNull public String toString() ## public static final class net.corda.core.flows.FlowStackSnapshot$Frame extends java.lang.Object @@ -2285,10 +2087,6 @@ public static final class net.corda.core.flows.FlowStackSnapshot$Frame extends j @NotNull public String toString() ## -public interface net.corda.core.flows.IdentifiableException - @Nullable - public Long getErrorId() -## @CordaSerializable public final class net.corda.core.flows.IllegalFlowLogicException extends java.lang.IllegalArgumentException public (Class, String) @@ -2321,7 +2119,6 @@ public final class net.corda.core.flows.NotarisationPayload extends java.lang.Ob @NotNull public final Object getTransaction() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2348,7 +2145,6 @@ public final class net.corda.core.flows.NotarisationRequestSignature extends jav public final net.corda.core.crypto.DigitalSignature$WithKey getDigitalSignature() public final int getPlatformVersion() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -2362,7 +2158,6 @@ public final class net.corda.core.flows.NotarisationResponse extends java.lang.O @NotNull public final java.util.List getSignatures() public int hashCode() - @NotNull public String toString() ## @InitiatingFlow @@ -2503,20 +2298,6 @@ public static final class net.corda.core.flows.NotaryFlow$Client$Companion$REQUE public static final class net.corda.core.flows.NotaryFlow$Client$Companion$VALIDATING extends net.corda.core.utilities.ProgressTracker$Step public static final net.corda.core.flows.NotaryFlow$Client$Companion$VALIDATING INSTANCE ## -public final class net.corda.core.flows.ReceiveFinalityFlow extends net.corda.core.flows.FlowLogic - public (net.corda.core.flows.FlowSession) - public (net.corda.core.flows.FlowSession, net.corda.core.crypto.SecureHash) - public (net.corda.core.flows.FlowSession, net.corda.core.crypto.SecureHash, net.corda.core.node.StatesToRecord) - @Suspendable - @NotNull - public net.corda.core.transactions.SignedTransaction call() - @Nullable - public final net.corda.core.crypto.SecureHash getExpectedTxId() - @NotNull - public final net.corda.core.flows.FlowSession getOtherSideSession() - @NotNull - public final net.corda.core.node.StatesToRecord getStatesToRecord() -## public final class net.corda.core.flows.ReceiveStateAndRefFlow extends net.corda.core.flows.FlowLogic public (net.corda.core.flows.FlowSession) @Suspendable @@ -2530,12 +2311,6 @@ public class net.corda.core.flows.ReceiveTransactionFlow extends net.corda.core. @Suspendable @NotNull public net.corda.core.transactions.SignedTransaction call() - @Suspendable - protected void checkBeforeRecording(net.corda.core.transactions.SignedTransaction) -## -@CordaSerializable -public final class net.corda.core.flows.RetrieveAnyTransactionPayload extends java.util.ArrayList - public static final net.corda.core.flows.RetrieveAnyTransactionPayload INSTANCE ## public @interface net.corda.core.flows.SchedulableFlow ## @@ -2546,7 +2321,6 @@ public class net.corda.core.flows.SendTransactionFlow extends net.corda.core.flo public (net.corda.core.flows.FlowSession, net.corda.core.transactions.SignedTransaction) ## public abstract class net.corda.core.flows.SignTransactionFlow extends net.corda.core.flows.FlowLogic - public (net.corda.core.flows.FlowSession) public (net.corda.core.flows.FlowSession, net.corda.core.utilities.ProgressTracker) @Suspendable @NotNull @@ -2587,7 +2361,6 @@ public final class net.corda.core.flows.StackFrameDataToken extends java.lang.Ob @NotNull public final String getClassName() public int hashCode() - @NotNull public String toString() ## public @interface net.corda.core.flows.StartableByRPC @@ -2597,31 +2370,17 @@ public @interface net.corda.core.flows.StartableByService @CordaSerializable public final class net.corda.core.flows.StateConsumptionDetails extends java.lang.Object public (net.corda.core.crypto.SecureHash) - public (net.corda.core.crypto.SecureHash, net.corda.core.flows.StateConsumptionDetails$ConsumedStateType) @NotNull public final net.corda.core.crypto.SecureHash component1() @NotNull - public final net.corda.core.flows.StateConsumptionDetails$ConsumedStateType component2() - @NotNull public final net.corda.core.flows.StateConsumptionDetails copy(net.corda.core.crypto.SecureHash) - @NotNull - public final net.corda.core.flows.StateConsumptionDetails copy(net.corda.core.crypto.SecureHash, net.corda.core.flows.StateConsumptionDetails$ConsumedStateType) public boolean equals(Object) @NotNull public final net.corda.core.crypto.SecureHash getHashOfTransactionId() - @NotNull - public final net.corda.core.flows.StateConsumptionDetails$ConsumedStateType getType() public int hashCode() - @NotNull public String toString() ## @CordaSerializable -public static final class net.corda.core.flows.StateConsumptionDetails$ConsumedStateType extends java.lang.Enum - protected () - public static net.corda.core.flows.StateConsumptionDetails$ConsumedStateType valueOf(String) - public static net.corda.core.flows.StateConsumptionDetails$ConsumedStateType[] values() -## -@CordaSerializable public final class net.corda.core.flows.StateMachineRunId extends java.lang.Object public (java.util.UUID) @NotNull @@ -2650,68 +2409,6 @@ public class net.corda.core.flows.StateReplacementException extends net.corda.co public final class net.corda.core.flows.UnexpectedFlowEndException extends net.corda.core.CordaRuntimeException implements net.corda.core.flows.IdentifiableException public (String) public (String, Throwable) - public (String, Throwable, Long) - @Nullable - public Long getErrorId() - @Nullable - public final Long getOriginalErrorId() -## -public final class net.corda.core.flows.WithReferencedStatesFlow extends net.corda.core.flows.FlowLogic - public (net.corda.core.flows.FlowLogic, net.corda.core.utilities.ProgressTracker) - @Suspendable - @NotNull - public T call() - @NotNull - public final net.corda.core.flows.FlowLogic getFlowLogic() - @NotNull - public net.corda.core.utilities.ProgressTracker getProgressTracker() - @NotNull - public static final net.corda.core.utilities.ProgressTracker tracker() - public static final net.corda.core.flows.WithReferencedStatesFlow$Companion Companion -## -public static final class net.corda.core.flows.WithReferencedStatesFlow$Companion extends java.lang.Object - @NotNull - public final org.slf4j.Logger getLogger() - @NotNull - public final net.corda.core.utilities.ProgressTracker tracker() -## -@CordaSerializable -public static final class net.corda.core.flows.WithReferencedStatesFlow$Companion$ATTEMPT extends net.corda.core.utilities.ProgressTracker$Step - public static final net.corda.core.flows.WithReferencedStatesFlow$Companion$ATTEMPT INSTANCE -## -@CordaSerializable -public static final class net.corda.core.flows.WithReferencedStatesFlow$Companion$RETRYING extends net.corda.core.utilities.ProgressTracker$Step - public static final net.corda.core.flows.WithReferencedStatesFlow$Companion$RETRYING INSTANCE -## -@CordaSerializable -public static final class net.corda.core.flows.WithReferencedStatesFlow$Companion$SUCCESS extends net.corda.core.utilities.ProgressTracker$Step - public static final net.corda.core.flows.WithReferencedStatesFlow$Companion$SUCCESS INSTANCE -## -public static final class net.corda.core.flows.WithReferencedStatesFlow$FlowResult$Conflict extends net.corda.core.flows.WithReferencedStatesFlow$FlowResult - public (java.util.Set) - @NotNull - public final java.util.Set component1() - @NotNull - public final net.corda.core.flows.WithReferencedStatesFlow$FlowResult$Conflict copy(java.util.Set) - public boolean equals(Object) - @NotNull - public final java.util.Set getStateRefs() - public int hashCode() - @NotNull - public String toString() -## -public static final class net.corda.core.flows.WithReferencedStatesFlow$FlowResult$Success extends net.corda.core.flows.WithReferencedStatesFlow$FlowResult - public (T) - @NotNull - public final T component1() - @NotNull - public final net.corda.core.flows.WithReferencedStatesFlow$FlowResult$Success copy(T) - public boolean equals(Object) - @NotNull - public final T getValue() - public int hashCode() - @NotNull - public String toString() ## @DoNotImplement @CordaSerializable @@ -2869,7 +2566,6 @@ public final class net.corda.core.messaging.ClientRpcSslOptions extends java.lan @NotNull public final String getTrustStoreProvider() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -2881,8 +2577,6 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes @NotNull public abstract java.time.Instant currentNodeTime() @NotNull - public abstract net.corda.core.node.NetworkParameters getNetworkParameters() - @NotNull public abstract Iterable getVaultTransactionNotes(net.corda.core.crypto.SecureHash) @RPCReturnsObservables @NotNull @@ -2890,8 +2584,6 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes @NotNull public abstract java.util.List internalVerifiedTransactionsSnapshot() public abstract boolean isFlowsDrainingModeEnabled() - public abstract boolean isWaitingForShutdown() - public abstract boolean killFlow(net.corda.core.flows.StateMachineRunId) @RPCReturnsObservables @NotNull public abstract net.corda.core.messaging.DataFeed, net.corda.core.node.services.NetworkMapCache$MapChange> networkMapFeed() @@ -2916,11 +2608,9 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes public abstract net.corda.core.identity.Party partyFromKey(java.security.PublicKey) @NotNull public abstract java.util.List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort) - public abstract void refreshNetworkMapCache() @NotNull public abstract java.util.List registeredFlows() public abstract void setFlowsDrainingModeEnabled(boolean) - public abstract void shutdown() @RPCReturnsObservables @NotNull public abstract net.corda.core.messaging.FlowHandle startFlowDynamic(Class>, Object...) @@ -2937,7 +2627,6 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes public abstract net.corda.core.messaging.DataFeed, net.corda.core.messaging.StateMachineUpdate> stateMachinesFeed() @NotNull public abstract java.util.List stateMachinesSnapshot() - public abstract void terminate(boolean) @NotNull public abstract net.corda.core.crypto.SecureHash uploadAttachment(java.io.InputStream) @NotNull @@ -2987,11 +2676,9 @@ public final class net.corda.core.messaging.DataFeed extends java.lang.Object @NotNull public final rx.Observable getUpdates() public int hashCode() - @NotNull public String toString() ## @DoNotImplement -@CordaSerializable public interface net.corda.core.messaging.FlowHandle extends java.lang.AutoCloseable public abstract void close() @NotNull @@ -3016,11 +2703,9 @@ public final class net.corda.core.messaging.FlowHandleImpl extends java.lang.Obj @NotNull public net.corda.core.concurrent.CordaFuture getReturnValue() public int hashCode() - @NotNull public String toString() ## @DoNotImplement -@CordaSerializable public interface net.corda.core.messaging.FlowProgressHandle extends net.corda.core.messaging.FlowHandle public abstract void close() @NotNull @@ -3063,7 +2748,6 @@ public final class net.corda.core.messaging.FlowProgressHandleImpl extends java. @Nullable public net.corda.core.messaging.DataFeed getStepsTreeIndexFeed() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3095,7 +2779,6 @@ public final class net.corda.core.messaging.ParametersUpdateInfo extends java.la @NotNull public final java.time.Instant getUpdateDeadline() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -3155,7 +2838,6 @@ public final class net.corda.core.messaging.StateMachineTransactionMapping exten @NotNull public final net.corda.core.crypto.SecureHash getTransactionId() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3176,7 +2858,6 @@ public static final class net.corda.core.messaging.StateMachineUpdate$Added exte @NotNull public final net.corda.core.messaging.StateMachineInfo getStateMachineInfo() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3194,7 +2875,6 @@ public static final class net.corda.core.messaging.StateMachineUpdate$Removed ex @NotNull public final net.corda.core.utilities.Try getResult() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -3205,27 +2885,8 @@ public interface net.corda.core.node.AppServiceHub extends net.corda.core.node.S public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic) ## @CordaSerializable -public final class net.corda.core.node.JavaPackageName extends java.lang.Object - public (String) - @NotNull - public final String component1() - @NotNull - public final net.corda.core.node.JavaPackageName copy(String) - public boolean equals(Object) - @NotNull - public final String getName() - public int hashCode() - public final boolean owns(String) - @NotNull - public String toString() -## -@CordaSerializable public final class net.corda.core.node.NetworkParameters extends java.lang.Object - @DeprecatedConstructorForDeserialization public (int, java.util.List, int, int, java.time.Instant, int, java.util.Map>) - @DeprecatedConstructorForDeserialization - public (int, java.util.List, int, int, java.time.Instant, int, java.util.Map>, java.time.Duration) - public (int, java.util.List, int, int, java.time.Instant, int, java.util.Map>, java.time.Duration, java.util.Map) public final int component1() @NotNull public final java.util.List component2() @@ -3237,19 +2898,9 @@ public final class net.corda.core.node.NetworkParameters extends java.lang.Objec @NotNull public final java.util.Map> component7() @NotNull - public final java.time.Duration component8() - @NotNull - public final java.util.Map component9() - @NotNull public final net.corda.core.node.NetworkParameters copy(int, java.util.List, int, int, java.time.Instant, int, java.util.Map>) - @NotNull - public final net.corda.core.node.NetworkParameters copy(int, java.util.List, int, int, java.time.Instant, int, java.util.Map>, java.time.Duration) - @NotNull - public final net.corda.core.node.NetworkParameters copy(int, java.util.List, int, int, java.time.Instant, int, java.util.Map>, java.time.Duration, java.util.Map) public boolean equals(Object) public final int getEpoch() - @NotNull - public final java.time.Duration getEventHorizon() public final int getMaxMessageSize() public final int getMaxTransactionSize() public final int getMinimumPlatformVersion() @@ -3257,18 +2908,11 @@ public final class net.corda.core.node.NetworkParameters extends java.lang.Objec public final java.time.Instant getModifiedTime() @NotNull public final java.util.List getNotaries() - @Nullable - public final java.security.PublicKey getOwnerOf(String) - @NotNull - public final java.util.Map getPackageOwnership() @NotNull public final java.util.Map> getWhitelistedContractImplementations() public int hashCode() - @NotNull public String toString() ## -public final class net.corda.core.node.NetworkParametersKt extends java.lang.Object -## @CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object public (java.util.List, java.util.List, int, long) @@ -3295,7 +2939,6 @@ public final class net.corda.core.node.NodeInfo extends java.lang.Object @NotNull public final net.corda.core.identity.Party identityFromX500Name(net.corda.core.identity.CordaX500Name) public final boolean isLegalIdentity(net.corda.core.identity.Party) - @NotNull public String toString() ## @CordaSerializable @@ -3311,7 +2954,6 @@ public final class net.corda.core.node.NotaryInfo extends java.lang.Object public final net.corda.core.identity.Party getIdentity() public final boolean getValidating() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -3331,8 +2973,6 @@ public interface net.corda.core.node.ServiceHub extends net.corda.core.node.Serv @NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @NotNull - public abstract net.corda.core.cordapp.CordappContext getAppContext() - @NotNull public abstract java.time.Clock getClock() @NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService() @@ -3364,9 +3004,6 @@ public interface net.corda.core.node.ServiceHub extends net.corda.core.node.Serv public abstract net.corda.core.transactions.SignedTransaction signInitialTransaction(net.corda.core.transactions.TransactionBuilder, java.security.PublicKey) @NotNull public abstract net.corda.core.contracts.StateAndRef toStateAndRef(net.corda.core.contracts.StateRef) - public abstract void withEntityManager(java.util.function.Consumer) - @NotNull - public abstract T withEntityManager(kotlin.jvm.functions.Function1) ## @DoNotImplement public interface net.corda.core.node.ServicesForResolution @@ -3388,10 +3025,6 @@ public final class net.corda.core.node.StatesToRecord extends java.lang.Enum public static net.corda.core.node.StatesToRecord valueOf(String) public static net.corda.core.node.StatesToRecord[] values() ## -@CordaSerializable -public final class net.corda.core.node.ZoneVersionTooLowException extends net.corda.core.CordaRuntimeException - public (String) -## @DoNotImplement public interface net.corda.core.node.services.AttachmentStorage public abstract boolean hasAttachment(net.corda.core.crypto.SecureHash) @@ -3485,7 +3118,6 @@ public static final class net.corda.core.node.services.NetworkMapCache$MapChange @NotNull public net.corda.core.node.NodeInfo getNode() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3503,7 +3135,6 @@ public static final class net.corda.core.node.services.NetworkMapCache$MapChange @NotNull public final net.corda.core.node.NodeInfo getPreviousNode() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3517,7 +3148,6 @@ public static final class net.corda.core.node.services.NetworkMapCache$MapChange @NotNull public net.corda.core.node.NodeInfo getNode() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -3566,7 +3196,6 @@ public static final class net.corda.core.node.services.PartyInfo$DistributedNode @NotNull public net.corda.core.identity.Party getParty() public int hashCode() - @NotNull public String toString() ## public static final class net.corda.core.node.services.PartyInfo$SingleNode extends net.corda.core.node.services.PartyInfo @@ -3583,7 +3212,6 @@ public static final class net.corda.core.node.services.PartyInfo$SingleNode exte @NotNull public net.corda.core.identity.Party getParty() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -3611,8 +3239,6 @@ public interface net.corda.core.node.services.TransactionStorage public abstract rx.Observable getUpdates() @NotNull public abstract net.corda.core.messaging.DataFeed, net.corda.core.transactions.SignedTransaction> track() - @NotNull - public abstract net.corda.core.concurrent.CordaFuture trackTransaction(net.corda.core.crypto.SecureHash) ## @DoNotImplement public interface net.corda.core.node.services.TransactionVerifierService @@ -3637,35 +3263,6 @@ public static final class net.corda.core.node.services.Vault$Companion extends j public final net.corda.core.node.services.Vault$Update getNoUpdate() ## @CordaSerializable -public static final class net.corda.core.node.services.Vault$ConstraintInfo extends java.lang.Object - public (net.corda.core.contracts.AttachmentConstraint) - @NotNull - public final net.corda.core.contracts.AttachmentConstraint component1() - @NotNull - public final net.corda.core.node.services.Vault$ConstraintInfo copy(net.corda.core.contracts.AttachmentConstraint) - @Nullable - public final byte[] data() - public boolean equals(Object) - @NotNull - public final net.corda.core.contracts.AttachmentConstraint getConstraint() - public int hashCode() - @NotNull - public String toString() - @NotNull - public final net.corda.core.node.services.Vault$ConstraintInfo$Type type() - public static final net.corda.core.node.services.Vault$ConstraintInfo$Companion Companion -## -public static final class net.corda.core.node.services.Vault$ConstraintInfo$Companion extends java.lang.Object - @NotNull - public final net.corda.core.node.services.Vault$ConstraintInfo constraintInfo(net.corda.core.node.services.Vault$ConstraintInfo$Type, byte[]) -## -@CordaSerializable -public static final class net.corda.core.node.services.Vault$ConstraintInfo$Type extends java.lang.Enum - protected () - public static net.corda.core.node.services.Vault$ConstraintInfo$Type valueOf(String) - public static net.corda.core.node.services.Vault$ConstraintInfo$Type[] values() -## -@CordaSerializable public static final class net.corda.core.node.services.Vault$Page extends java.lang.Object public (java.util.List>, java.util.List, long, net.corda.core.node.services.Vault$StateStatus, java.util.List) @NotNull @@ -3690,16 +3287,9 @@ public static final class net.corda.core.node.services.Vault$Page extends java.l public final java.util.List getStatesMetadata() public final long getTotalStatesAvailable() public int hashCode() - @NotNull public String toString() ## @CordaSerializable -public static final class net.corda.core.node.services.Vault$RelevancyStatus extends java.lang.Enum - protected () - public static net.corda.core.node.services.Vault$RelevancyStatus valueOf(String) - public static net.corda.core.node.services.Vault$RelevancyStatus[] values() -## -@CordaSerializable public static final class net.corda.core.node.services.Vault$StateMetadata extends java.lang.Object public (net.corda.core.contracts.StateRef, String, java.time.Instant, java.time.Instant, net.corda.core.node.services.Vault$StateStatus, net.corda.core.identity.AbstractParty, String, java.time.Instant) public (net.corda.core.contracts.StateRef, String, java.time.Instant, java.time.Instant, net.corda.core.node.services.Vault$StateStatus, net.corda.core.identity.AbstractParty, String, java.time.Instant, net.corda.core.node.services.Vault$RelevancyStatus) @@ -3843,7 +3433,6 @@ public interface net.corda.core.node.services.VaultService public abstract net.corda.core.concurrent.CordaFuture> whenConsumed(net.corda.core.contracts.StateRef) ## public final class net.corda.core.node.services.VaultServiceKt extends java.lang.Object - public static final int MAX_CONSTRAINT_DATA_SIZE = 563 ## @CordaSerializable public final class net.corda.core.node.services.vault.AggregateFunctionType extends java.lang.Enum @@ -3890,7 +3479,6 @@ public static final class net.corda.core.node.services.vault.AttachmentQueryCrit @Nullable public final net.corda.core.node.services.vault.ColumnPredicate getUploaderCondition() public int hashCode() - @NotNull public String toString() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser) @@ -3916,7 +3504,6 @@ public final class net.corda.core.node.services.vault.AttachmentSort extends net @NotNull public final java.util.Collection getColumns() public int hashCode() - @NotNull public String toString() ## public static final class net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute extends java.lang.Enum @@ -3941,7 +3528,6 @@ public static final class net.corda.core.node.services.vault.AttachmentSort$Atta @NotNull public final net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute getSortAttribute() public int hashCode() - @NotNull public String toString() ## public interface net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser extends net.corda.core.node.services.vault.BaseQueryCriteriaParser @@ -3984,88 +3570,50 @@ public final class net.corda.core.node.services.vault.Builder extends java.lang. @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression avg(kotlin.reflect.KProperty1, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression avg(net.corda.core.node.services.vault.FieldInfo) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression avg(net.corda.core.node.services.vault.FieldInfo, java.util.List) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression avg(net.corda.core.node.services.vault.FieldInfo, java.util.List, net.corda.core.node.services.vault.Sort$Direction) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$Between between(R, R) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression between(reflect.Field, R, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression between(kotlin.reflect.KProperty1, R, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression between(net.corda.core.node.services.vault.FieldInfo, R, R) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison compare(net.corda.core.node.services.vault.BinaryComparisonOperator, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression comparePredicate(reflect.Field, net.corda.core.node.services.vault.BinaryComparisonOperator, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression comparePredicate(kotlin.reflect.KProperty1, net.corda.core.node.services.vault.BinaryComparisonOperator, R) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression comparePredicate(net.corda.core.node.services.vault.FieldInfo, net.corda.core.node.services.vault.BinaryComparisonOperator, R) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression count(reflect.Field) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression count(kotlin.reflect.KProperty1) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression count(net.corda.core.node.services.vault.FieldInfo) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$EqualityComparison equal(R) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$EqualityComparison equal(R, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(reflect.Field, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(reflect.Field, R, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(kotlin.reflect.KProperty1, R) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(kotlin.reflect.KProperty1, R, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression equal(net.corda.core.node.services.vault.FieldInfo, R, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression functionPredicate(reflect.Field, net.corda.core.node.services.vault.ColumnPredicate, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression functionPredicate(kotlin.reflect.KProperty1, net.corda.core.node.services.vault.ColumnPredicate, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression functionPredicate(net.corda.core.node.services.vault.FieldInfo, net.corda.core.node.services.vault.ColumnPredicate, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison greaterThan(R) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThan(reflect.Field, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThan(kotlin.reflect.KProperty1, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThan(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison greaterThanOrEqual(R) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThanOrEqual(reflect.Field, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThanOrEqual(kotlin.reflect.KProperty1, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression greaterThanOrEqual(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(reflect.Field, java.util.Collection) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(reflect.Field, java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression in(java.util.Collection) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression in(java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(kotlin.reflect.KProperty1, java.util.Collection) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(kotlin.reflect.KProperty1, java.util.Collection, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(net.corda.core.node.services.vault.FieldInfo, java.util.Collection) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(net.corda.core.node.services.vault.FieldInfo, java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$NullExpression isNotNull() @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$NullExpression isNull() @@ -4074,40 +3622,24 @@ public final class net.corda.core.node.services.vault.Builder extends java.lang. @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression isNull(kotlin.reflect.KProperty1) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression isNull(net.corda.core.node.services.vault.FieldInfo) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison lessThan(R) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThan(reflect.Field, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThan(kotlin.reflect.KProperty1, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThan(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison lessThanOrEqual(R) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThanOrEqual(reflect.Field, R) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThanOrEqual(kotlin.reflect.KProperty1, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThanOrEqual(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$Likeness like(String) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$Likeness like(String, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(reflect.Field, String) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(reflect.Field, String, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(kotlin.reflect.KProperty1, String) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(kotlin.reflect.KProperty1, String, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(net.corda.core.node.services.vault.FieldInfo, String) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(net.corda.core.node.services.vault.FieldInfo, String, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(reflect.Field) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(reflect.Field, java.util.List) @@ -4116,12 +3648,6 @@ public final class net.corda.core.node.services.vault.Builder extends java.lang. @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(kotlin.reflect.KProperty1, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(net.corda.core.node.services.vault.FieldInfo) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(net.corda.core.node.services.vault.FieldInfo, java.util.List) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(net.corda.core.node.services.vault.FieldInfo, java.util.List, net.corda.core.node.services.vault.Sort$Direction) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(reflect.Field) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(reflect.Field, java.util.List) @@ -4130,72 +3656,32 @@ public final class net.corda.core.node.services.vault.Builder extends java.lang. @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(kotlin.reflect.KProperty1, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(net.corda.core.node.services.vault.FieldInfo) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(net.corda.core.node.services.vault.FieldInfo, java.util.List) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression min(net.corda.core.node.services.vault.FieldInfo, java.util.List, net.corda.core.node.services.vault.Sort$Direction) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$EqualityComparison notEqual(R) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$EqualityComparison notEqual(R, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(reflect.Field, R) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(reflect.Field, R, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(kotlin.reflect.KProperty1, R) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(kotlin.reflect.KProperty1, R, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(net.corda.core.node.services.vault.FieldInfo, R) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notEqual(net.corda.core.node.services.vault.FieldInfo, R, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(reflect.Field, java.util.Collection) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(reflect.Field, java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression notIn(java.util.Collection) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression notIn(java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(kotlin.reflect.KProperty1, java.util.Collection) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(kotlin.reflect.KProperty1, java.util.Collection, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(net.corda.core.node.services.vault.FieldInfo, java.util.Collection) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(net.corda.core.node.services.vault.FieldInfo, java.util.Collection, boolean) - @NotNull public final net.corda.core.node.services.vault.ColumnPredicate$Likeness notLike(String) @NotNull - public final net.corda.core.node.services.vault.ColumnPredicate$Likeness notLike(String, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(reflect.Field, String) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(reflect.Field, String, boolean) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(kotlin.reflect.KProperty1, String) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(kotlin.reflect.KProperty1, String, boolean) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(net.corda.core.node.services.vault.FieldInfo, String) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(net.corda.core.node.services.vault.FieldInfo, String, boolean) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notNull(reflect.Field) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notNull(kotlin.reflect.KProperty1) @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notNull(net.corda.core.node.services.vault.FieldInfo) - @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression predicate(reflect.Field, net.corda.core.node.services.vault.ColumnPredicate) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression predicate(kotlin.reflect.KProperty1, net.corda.core.node.services.vault.ColumnPredicate) @NotNull - public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression predicate(net.corda.core.node.services.vault.FieldInfo, net.corda.core.node.services.vault.ColumnPredicate) - @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(reflect.Field) @NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(reflect.Field, java.util.List) @@ -4203,12 +3689,6 @@ public final class net.corda.core.node.services.vault.Builder extends java.lang. public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(reflect.Field, java.util.List, net.corda.core.node.services.vault.Sort$Direction) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(kotlin.reflect.KProperty1, java.util.List>, net.corda.core.node.services.vault.Sort$Direction) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(net.corda.core.node.services.vault.FieldInfo) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(net.corda.core.node.services.vault.FieldInfo, java.util.List) - @NotNull - public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression sum(net.corda.core.node.services.vault.FieldInfo, java.util.List, net.corda.core.node.services.vault.Sort$Direction) public static final net.corda.core.node.services.vault.Builder INSTANCE ## @DoNotImplement @@ -4223,12 +3703,10 @@ public final class net.corda.core.node.services.vault.Column extends java.lang.O public (String, Class) public (reflect.Field) public (kotlin.reflect.KProperty1) - public (net.corda.core.node.services.vault.FieldInfo) @NotNull public final Class getDeclaringClass() @NotNull public final String getName() - public static final net.corda.core.node.services.vault.Column$Companion Companion ## @CordaSerializable public abstract class net.corda.core.node.services.vault.ColumnPredicate extends java.lang.Object @@ -4244,7 +3722,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Agg @NotNull public final net.corda.core.node.services.vault.AggregateFunctionType getType() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4262,7 +3739,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Bet @NotNull public final C getRightToLiteral() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4280,7 +3756,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Bin @NotNull public final C getRightLiteral() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4298,7 +3773,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Col @NotNull public final java.util.Collection getRightLiteral() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4314,7 +3788,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Equ public final net.corda.core.node.services.vault.EqualityComparisonOperator getOperator() public final C getRightLiteral() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4332,7 +3805,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Lik @NotNull public final String getRightLiteral() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4346,7 +3818,6 @@ public static final class net.corda.core.node.services.vault.ColumnPredicate$Nul @NotNull public final net.corda.core.node.services.vault.NullOperator getOperator() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4375,7 +3846,6 @@ public static final class net.corda.core.node.services.vault.CriteriaExpression$ @NotNull public final net.corda.core.node.services.vault.ColumnPredicate getPredicate() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4397,7 +3867,6 @@ public static final class net.corda.core.node.services.vault.CriteriaExpression$ @NotNull public final net.corda.core.node.services.vault.CriteriaExpression getRight() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4415,7 +3884,6 @@ public static final class net.corda.core.node.services.vault.CriteriaExpression$ @NotNull public final net.corda.core.node.services.vault.ColumnPredicate getPredicate() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4429,7 +3897,6 @@ public static final class net.corda.core.node.services.vault.CriteriaExpression$ @NotNull public final net.corda.core.node.services.vault.CriteriaExpression getExpression() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -4439,13 +3906,6 @@ public final class net.corda.core.node.services.vault.EqualityComparisonOperator public static net.corda.core.node.services.vault.EqualityComparisonOperator valueOf(String) public static net.corda.core.node.services.vault.EqualityComparisonOperator[] values() ## -public final class net.corda.core.node.services.vault.FieldInfo extends java.lang.Object - public (String, Class) - @NotNull - public final Class getEntityClass() - @NotNull - public final String getName() -## public interface net.corda.core.node.services.vault.GenericQueryCriteria @NotNull public abstract java.util.Collection visit(P) @@ -4516,7 +3976,6 @@ public final class net.corda.core.node.services.vault.PageSpecification extends public final int getPageSize() public int hashCode() public final boolean isDefault() - @NotNull public String toString() ## @CordaSerializable @@ -4539,15 +3998,9 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$AndCo @CordaSerializable public abstract static class net.corda.core.node.services.vault.QueryCriteria$CommonQueryCriteria extends net.corda.core.node.services.vault.QueryCriteria public () - @NotNull - public java.util.Set getConstraintTypes() - @NotNull - public java.util.Set getConstraints() @Nullable public abstract java.util.Set> getContractStateTypes() @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() - @NotNull public abstract net.corda.core.node.services.Vault$StateStatus getStatus() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @@ -4562,7 +4015,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Fungi public (java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List) public (java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus) public (java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - public (java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) @Nullable public final java.util.List component1() @Nullable @@ -4578,11 +4030,7 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Fungi @Nullable public final java.util.Set> component7() @NotNull - public final net.corda.core.node.services.Vault$RelevancyStatus component8() - @NotNull public final net.corda.core.node.services.vault.QueryCriteria$FungibleAssetQueryCriteria copy(java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - @NotNull - public final net.corda.core.node.services.vault.QueryCriteria$FungibleAssetQueryCriteria copy(java.util.List, java.util.List, net.corda.core.node.services.vault.ColumnPredicate, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) public boolean equals(Object) @Nullable public java.util.Set> getContractStateTypes() @@ -4597,44 +4045,8 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Fungi @Nullable public final net.corda.core.node.services.vault.ColumnPredicate getQuantity() @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() - @NotNull public net.corda.core.node.services.Vault$StateStatus getStatus() public int hashCode() - @NotNull - public String toString() - @NotNull - public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) -## -@CordaSerializable -public static final class net.corda.core.node.services.vault.QueryCriteria$FungibleStateQueryCriteria extends net.corda.core.node.services.vault.QueryCriteria$CommonQueryCriteria - public () - public (java.util.List, net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) - @Nullable - public final java.util.List component1() - @Nullable - public final net.corda.core.node.services.vault.ColumnPredicate component2() - @NotNull - public final net.corda.core.node.services.Vault$StateStatus component3() - @Nullable - public final java.util.Set> component4() - @NotNull - public final net.corda.core.node.services.Vault$RelevancyStatus component5() - @NotNull - public final net.corda.core.node.services.vault.QueryCriteria$FungibleStateQueryCriteria copy(java.util.List, net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) - public boolean equals(Object) - @Nullable - public java.util.Set> getContractStateTypes() - @Nullable - public final java.util.List getParticipants() - @Nullable - public final net.corda.core.node.services.vault.ColumnPredicate getQuantity() - @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() - @NotNull - public net.corda.core.node.services.Vault$StateStatus getStatus() - public int hashCode() - @NotNull public String toString() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @@ -4647,9 +4059,7 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Linea public (java.util.List, java.util.List, java.util.List) public (java.util.List, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus) public (java.util.List, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - public (java.util.List, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) public (java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - public (java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) @Nullable public final java.util.List component1() @Nullable @@ -4661,11 +4071,7 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Linea @Nullable public final java.util.Set> component5() @NotNull - public final net.corda.core.node.services.Vault$RelevancyStatus component6() - @NotNull public final net.corda.core.node.services.vault.QueryCriteria$LinearStateQueryCriteria copy(java.util.List, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - @NotNull - public final net.corda.core.node.services.vault.QueryCriteria$LinearStateQueryCriteria copy(java.util.List, java.util.List, java.util.List, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) public boolean equals(Object) @Nullable public java.util.Set> getContractStateTypes() @@ -4674,13 +4080,10 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Linea @Nullable public final java.util.List getParticipants() @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() - @NotNull public net.corda.core.node.services.Vault$StateStatus getStatus() @Nullable public final java.util.List getUuid() public int hashCode() - @NotNull public String toString() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @@ -4710,7 +4113,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$SoftL @NotNull public final net.corda.core.node.services.vault.QueryCriteria$SoftLockingType getType() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4734,7 +4136,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$TimeC @NotNull public final net.corda.core.node.services.vault.QueryCriteria$TimeInstantType getType() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4748,7 +4149,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Vault public (net.corda.core.node.services.vault.CriteriaExpression) public (net.corda.core.node.services.vault.CriteriaExpression, net.corda.core.node.services.Vault$StateStatus) public (net.corda.core.node.services.vault.CriteriaExpression, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - public (net.corda.core.node.services.vault.CriteriaExpression, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) @NotNull public final net.corda.core.node.services.vault.CriteriaExpression component1() @NotNull @@ -4756,22 +4156,15 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Vault @Nullable public final java.util.Set> component3() @NotNull - public final net.corda.core.node.services.Vault$RelevancyStatus component4() - @NotNull public final net.corda.core.node.services.vault.QueryCriteria$VaultCustomQueryCriteria copy(net.corda.core.node.services.vault.CriteriaExpression, net.corda.core.node.services.Vault$StateStatus, java.util.Set>) - @NotNull - public final net.corda.core.node.services.vault.QueryCriteria$VaultCustomQueryCriteria copy(net.corda.core.node.services.vault.CriteriaExpression, net.corda.core.node.services.Vault$StateStatus, java.util.Set>, net.corda.core.node.services.Vault$RelevancyStatus) public boolean equals(Object) @Nullable public java.util.Set> getContractStateTypes() @NotNull public final net.corda.core.node.services.vault.CriteriaExpression getExpression() @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() - @NotNull public net.corda.core.node.services.Vault$StateStatus getStatus() public int hashCode() - @NotNull public String toString() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @@ -4785,9 +4178,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Vault public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List) public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition) public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition) - public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition, net.corda.core.node.services.Vault$RelevancyStatus) - public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition, net.corda.core.node.services.Vault$RelevancyStatus, java.util.Set) - public (net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition, net.corda.core.node.services.Vault$RelevancyStatus, java.util.Set, java.util.Set) @NotNull public final net.corda.core.node.services.Vault$StateStatus component1() @Nullable @@ -4801,26 +4191,12 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Vault @Nullable public final net.corda.core.node.services.vault.QueryCriteria$TimeCondition component6() @NotNull - public final net.corda.core.node.services.Vault$RelevancyStatus component7() - @NotNull - public final java.util.Set component8() - @NotNull - public final java.util.Set component9() - @NotNull public final net.corda.core.node.services.vault.QueryCriteria$VaultQueryCriteria copy(net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition) - @NotNull - public final net.corda.core.node.services.vault.QueryCriteria$VaultQueryCriteria copy(net.corda.core.node.services.Vault$StateStatus, java.util.Set>, java.util.List, java.util.List, net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition, net.corda.core.node.services.vault.QueryCriteria$TimeCondition, net.corda.core.node.services.Vault$RelevancyStatus, java.util.Set, java.util.Set) public boolean equals(Object) - @NotNull - public java.util.Set getConstraintTypes() - @NotNull - public java.util.Set getConstraints() @Nullable public java.util.Set> getContractStateTypes() @Nullable public final java.util.List getNotary() - @NotNull - public net.corda.core.node.services.Vault$RelevancyStatus getRelevancyStatus() @Nullable public final net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition getSoftLockingCondition() @Nullable @@ -4830,7 +4206,6 @@ public static final class net.corda.core.node.services.vault.QueryCriteria$Vault @Nullable public final net.corda.core.node.services.vault.QueryCriteria$TimeCondition getTimeCondition() public int hashCode() - @NotNull public String toString() @NotNull public java.util.Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @@ -4840,8 +4215,6 @@ public final class net.corda.core.node.services.vault.QueryCriteriaUtils extends @NotNull public static final String getColumnName(net.corda.core.node.services.vault.Column) @NotNull - public static final net.corda.core.node.services.vault.FieldInfo getField(String, Class) - @NotNull public static final Class resolveEnclosingObjectFromColumn(net.corda.core.node.services.vault.Column) @NotNull public static final Class resolveEnclosingObjectFromExpression(net.corda.core.node.services.vault.CriteriaExpression) @@ -4860,7 +4233,6 @@ public final class net.corda.core.node.services.vault.Sort extends net.corda.cor @NotNull public final java.util.Collection getColumns() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -4917,7 +4289,6 @@ public static final class net.corda.core.node.services.vault.Sort$SortColumn ext @NotNull public final net.corda.core.node.services.vault.SortAttribute getSortAttribute() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -4947,7 +4318,6 @@ public static final class net.corda.core.node.services.vault.SortAttribute$Custo @NotNull public final String getEntityStateColumnName() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -4961,15 +4331,12 @@ public static final class net.corda.core.node.services.vault.SortAttribute$Stand @NotNull public final net.corda.core.node.services.vault.Sort$Attribute getAttribute() public int hashCode() - @NotNull public String toString() ## public final class net.corda.core.schemas.CommonSchema extends java.lang.Object public static final net.corda.core.schemas.CommonSchema INSTANCE ## public final class net.corda.core.schemas.CommonSchemaV1 extends net.corda.core.schemas.MappedSchema - @NotNull - public String getMigrationResource() public static final net.corda.core.schemas.CommonSchemaV1 INSTANCE ## @MappedSuperclass @@ -5010,34 +4377,14 @@ public static class net.corda.core.schemas.CommonSchemaV1$LinearState extends ne ## public class net.corda.core.schemas.MappedSchema extends java.lang.Object public (Class, int, Iterable>) - public boolean equals(Object) @NotNull public final Iterable> getMappedTypes() - @Nullable - public String getMigrationResource() @NotNull public final String getName() public final int getVersion() - public int hashCode() @NotNull public String toString() ## -public final class net.corda.core.schemas.MappedSchemaValidator extends java.lang.Object - @NotNull - public final java.util.List crossReferencesToOtherMappedSchema(net.corda.core.schemas.MappedSchema) - @NotNull - public final java.util.List fieldsFromOtherMappedSchema(net.corda.core.schemas.MappedSchema) - @NotNull - public final java.util.List methodsFromOtherMappedSchema(net.corda.core.schemas.MappedSchema) - public static final net.corda.core.schemas.MappedSchemaValidator INSTANCE -## -public static final class net.corda.core.schemas.MappedSchemaValidator$SchemaCrossReferenceReport extends java.lang.Object - public (String, String, String, String, String) - @NotNull - public String toString() - @NotNull - public final String toWarning() -## @MappedSuperclass @CordaSerializable public class net.corda.core.schemas.PersistentState extends java.lang.Object implements net.corda.core.schemas.StatePersistable @@ -5048,7 +4395,6 @@ public class net.corda.core.schemas.PersistentState extends java.lang.Object imp public void setStateRef(net.corda.core.schemas.PersistentStateRef) ## @Embeddable -@Immutable public class net.corda.core.schemas.PersistentStateRef extends java.lang.Object implements java.io.Serializable public () public (String, int) @@ -5065,7 +4411,6 @@ public class net.corda.core.schemas.PersistentStateRef extends java.lang.Object public int hashCode() public void setIndex(int) public void setTxId(String) - @NotNull public String toString() ## @CordaSerializable @@ -5082,11 +4427,6 @@ public interface net.corda.core.serialization.ClassWhitelist ## public @interface net.corda.core.serialization.ConstructorForDeserialization ## -public final class net.corda.core.serialization.ContextPropertyKeys extends java.lang.Enum - protected () - public static net.corda.core.serialization.ContextPropertyKeys valueOf(String) - public static net.corda.core.serialization.ContextPropertyKeys[] values() -## public @interface net.corda.core.serialization.CordaSerializable ## public @interface net.corda.core.serialization.CordaSerializationTransformEnumDefault @@ -5106,10 +4446,6 @@ public @interface net.corda.core.serialization.CordaSerializationTransformRename public @interface net.corda.core.serialization.DeprecatedConstructorForDeserialization public abstract int version() ## -@DoNotImplement -public interface net.corda.core.serialization.EncodingWhitelist - public abstract boolean acceptEncoding(net.corda.core.serialization.SerializationEncoding) -## @CordaSerializable public final class net.corda.core.serialization.MissingAttachmentsException extends net.corda.core.CordaException public (java.util.List) @@ -5130,24 +4466,15 @@ public final class net.corda.core.serialization.ObjectWithCompatibleContext exte @NotNull public final T getObj() public int hashCode() - @NotNull public String toString() ## -public @interface net.corda.core.serialization.SerializableCalculatedProperty -## public final class net.corda.core.serialization.SerializationAPIKt extends java.lang.Object @NotNull public static final net.corda.core.serialization.SerializedBytes serialize(T, net.corda.core.serialization.SerializationFactory, net.corda.core.serialization.SerializationContext) ## -@DoNotImplement public interface net.corda.core.serialization.SerializationContext @NotNull public abstract ClassLoader getDeserializationClassLoader() - @Nullable - public abstract net.corda.core.serialization.SerializationEncoding getEncoding() - @NotNull - public abstract net.corda.core.serialization.EncodingWhitelist getEncodingWhitelist() - public abstract boolean getLenientCarpenterEnabled() public abstract boolean getObjectReferencesEnabled() @NotNull public abstract net.corda.core.utilities.ByteSequence getPreferredSerializationVersion() @@ -5162,12 +4489,6 @@ public interface net.corda.core.serialization.SerializationContext @NotNull public abstract net.corda.core.serialization.SerializationContext withClassLoader(ClassLoader) @NotNull - public abstract net.corda.core.serialization.SerializationContext withEncoding(net.corda.core.serialization.SerializationEncoding) - @NotNull - public abstract net.corda.core.serialization.SerializationContext withEncodingWhitelist(net.corda.core.serialization.EncodingWhitelist) - @NotNull - public abstract net.corda.core.serialization.SerializationContext withLenientCarpenter() - @NotNull public abstract net.corda.core.serialization.SerializationContext withPreferredSerializationVersion(net.corda.core.utilities.ByteSequence) @NotNull public abstract net.corda.core.serialization.SerializationContext withProperty(Object, Object) @@ -5198,9 +4519,6 @@ public final class net.corda.core.serialization.SerializationDefaults extends ja public final net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT() public static final net.corda.core.serialization.SerializationDefaults INSTANCE ## -@DoNotImplement -public interface net.corda.core.serialization.SerializationEncoding -## public abstract class net.corda.core.serialization.SerializationFactory extends java.lang.Object public () public final T asCurrent(kotlin.jvm.functions.Function1) @@ -5248,9 +4566,6 @@ public final class net.corda.core.serialization.SerializedBytes extends net.cord public (byte[]) @NotNull public final net.corda.core.crypto.SecureHash getHash() - public static final net.corda.core.serialization.SerializedBytes$Companion Companion -## -public static final class net.corda.core.serialization.SerializedBytes$Companion extends java.lang.Object ## public final class net.corda.core.serialization.SingletonSerializationToken extends java.lang.Object implements net.corda.core.serialization.SerializationToken @NotNull @@ -5292,8 +4607,6 @@ public abstract class net.corda.core.transactions.BaseTransaction extends java.l @NotNull public abstract java.util.List> getOutputs() @NotNull - public abstract java.util.List getReferences() - @NotNull public final net.corda.core.contracts.StateAndRef outRef(int) @NotNull public final net.corda.core.contracts.StateAndRef outRef(net.corda.core.contracts.ContractState) @@ -5341,11 +4654,8 @@ public final class net.corda.core.transactions.ContractUpgradeFilteredTransactio @NotNull public java.util.List> getOutputs() @NotNull - public java.util.List getReferences() - @NotNull public final java.util.Map getVisibleComponents() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -5393,8 +4703,6 @@ public final class net.corda.core.transactions.ContractUpgradeLedgerTransaction @NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt() @NotNull - public java.util.List> getReferences() - @NotNull public java.util.Set getRequiredSigningKeys() @NotNull public java.util.List getSigs() @@ -5403,7 +4711,6 @@ public final class net.corda.core.transactions.ContractUpgradeLedgerTransaction @NotNull public final String getUpgradedContractClassName() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -5432,8 +4739,6 @@ public final class net.corda.core.transactions.ContractUpgradeWireTransaction ex @NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt() @NotNull - public java.util.List getReferences() - @NotNull public final java.util.List getSerializedComponents() @NotNull public final net.corda.core.crypto.SecureHash getUpgradedContractAttachmentId() @@ -5442,7 +4747,6 @@ public final class net.corda.core.transactions.ContractUpgradeWireTransaction ex public int hashCode() @NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, java.util.List) - @NotNull public String toString() ## public static final class net.corda.core.transactions.ContractUpgradeWireTransaction$Component extends java.lang.Enum @@ -5456,8 +4760,6 @@ public abstract class net.corda.core.transactions.CoreTransaction extends net.co public () @NotNull public abstract java.util.List getInputs() - @NotNull - public abstract java.util.List getReferences() ## @CordaSerializable public final class net.corda.core.transactions.FilteredComponentGroup extends net.corda.core.transactions.ComponentGroup @@ -5480,7 +4782,6 @@ public final class net.corda.core.transactions.FilteredComponentGroup extends ne @NotNull public final net.corda.core.crypto.PartialMerkleTree getPartialMerkleTree() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -5519,22 +4820,17 @@ public abstract class net.corda.core.transactions.FullTransaction extends net.co protected void checkBaseInvariants() @NotNull public abstract java.util.List> getInputs() - @NotNull - public abstract java.util.List> getReferences() ## @DoNotImplement @CordaSerializable public final class net.corda.core.transactions.LedgerTransaction extends net.corda.core.transactions.FullTransaction public (java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) public (java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters) - public (java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters, java.util.List>) @NotNull public final java.util.List> commandsOfType(Class) @NotNull public final java.util.List> component1() @NotNull - public final java.util.List> component10() - @NotNull public final java.util.List> component2() @NotNull public final java.util.List> component3() @@ -5549,27 +4845,9 @@ public final class net.corda.core.transactions.LedgerTransaction extends net.cor @NotNull public final net.corda.core.contracts.PrivacySalt component8() @NotNull - public final net.corda.core.transactions.LedgerTransaction copy() - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow) - @NotNull public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) @NotNull public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters) - @NotNull - public final net.corda.core.transactions.LedgerTransaction copy(java.util.List>, java.util.List>, java.util.List>, java.util.List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters, java.util.List>) public boolean equals(Object) @NotNull public final java.util.List> filterCommands(Class, java.util.function.Predicate) @@ -5578,20 +4856,12 @@ public final class net.corda.core.transactions.LedgerTransaction extends net.cor @NotNull public final java.util.List filterInputs(Class, java.util.function.Predicate) @NotNull - public final java.util.List> filterReferenceInputRefs(Class, java.util.function.Predicate) - @NotNull - public final java.util.List filterReferenceInputs(Class, java.util.function.Predicate) - @NotNull public final net.corda.core.contracts.Command findCommand(Class, java.util.function.Predicate) @NotNull public final net.corda.core.contracts.StateAndRef findInRef(Class, java.util.function.Predicate) @NotNull public final T findInput(Class, java.util.function.Predicate) @NotNull - public final T findReference(Class, java.util.function.Predicate) - @NotNull - public final net.corda.core.contracts.StateAndRef findReferenceInputRef(Class, java.util.function.Predicate) - @NotNull public final net.corda.core.contracts.Attachment getAttachment(int) @NotNull public final net.corda.core.contracts.Attachment getAttachment(net.corda.core.crypto.SecureHash) @@ -5615,12 +4885,6 @@ public final class net.corda.core.transactions.LedgerTransaction extends net.cor public java.util.List> getOutputs() @NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt() - @NotNull - public final net.corda.core.contracts.ContractState getReferenceInput(int) - @NotNull - public final java.util.List getReferenceStates() - @NotNull - public java.util.List> getReferences() @Nullable public final net.corda.core.contracts.TimeWindow getTimeWindow() @NotNull @@ -5632,11 +4896,6 @@ public final class net.corda.core.transactions.LedgerTransaction extends net.cor public final java.util.List> inRefsOfType(Class) @NotNull public final java.util.List inputsOfType(Class) - @NotNull - public final java.util.List> referenceInputRefsOfType(Class) - @NotNull - public final java.util.List referenceInputsOfType(Class) - @NotNull public String toString() public final void verify() public static final net.corda.core.transactions.LedgerTransaction$Companion Companion @@ -5659,7 +4918,6 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro @NotNull public final java.util.List getOutputs() public int hashCode() - @NotNull public String toString() ## @CordaSerializable @@ -5697,13 +4955,10 @@ public final class net.corda.core.transactions.NotaryChangeLedgerTransaction ext @NotNull public java.util.List> getOutputs() @NotNull - public java.util.List> getReferences() - @NotNull public java.util.Set getRequiredSigningKeys() @NotNull public java.util.List getSigs() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -5727,15 +4982,12 @@ public final class net.corda.core.transactions.NotaryChangeWireTransaction exten @NotNull public java.util.List> getOutputs() @NotNull - public java.util.List getReferences() - @NotNull public final java.util.List getSerializedComponents() public int hashCode() @NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServiceHub, java.util.List) @NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, java.util.List) - @NotNull public String toString() ## public static final class net.corda.core.transactions.NotaryChangeWireTransaction$Component extends java.lang.Enum @@ -5770,8 +5022,6 @@ public final class net.corda.core.transactions.SignedTransaction extends java.la @NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction getNotaryChangeTx() @NotNull - public final java.util.List getReferences() - @NotNull public java.util.Set getRequiredSigningKeys() @NotNull public java.util.List getSigs() @@ -5832,15 +5082,7 @@ public static final class net.corda.core.transactions.SignedTransaction$Signatur public class net.corda.core.transactions.TransactionBuilder extends java.lang.Object public () public (net.corda.core.identity.Party) - public (net.corda.core.identity.Party, java.util.UUID) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>, java.util.List>) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.contracts.TimeWindow) public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, java.util.List) - public (net.corda.core.identity.Party, java.util.UUID, java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, java.util.List, net.corda.core.node.ServiceHub) @NotNull public final net.corda.core.transactions.TransactionBuilder addAttachment(net.corda.core.crypto.SecureHash) @NotNull @@ -5852,8 +5094,6 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob @NotNull public net.corda.core.transactions.TransactionBuilder addInputState(net.corda.core.contracts.StateAndRef) @NotNull - public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.ContractState) - @NotNull public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.ContractState, String) @NotNull public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.ContractState, String, net.corda.core.contracts.AttachmentConstraint) @@ -5864,12 +5104,8 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob @NotNull public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.ContractState, String, net.corda.core.identity.Party, Integer, net.corda.core.contracts.AttachmentConstraint) @NotNull - public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.ContractState, net.corda.core.identity.Party) - @NotNull public final net.corda.core.transactions.TransactionBuilder addOutputState(net.corda.core.contracts.TransactionState) @NotNull - public net.corda.core.transactions.TransactionBuilder addReferenceState(net.corda.core.contracts.ReferencedStateAndRef) - @NotNull public final java.util.List attachments() @NotNull public final java.util.List> commands() @@ -5889,18 +5125,12 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob protected final java.util.List> getOutputs() @NotNull protected final net.corda.core.contracts.PrivacySalt getPrivacySalt() - @NotNull - protected final java.util.List getReferences() - @Nullable - protected final net.corda.core.node.ServiceHub getServiceHub() @Nullable protected final net.corda.core.contracts.TimeWindow getWindow() @NotNull public final java.util.List inputStates() @NotNull public final java.util.List> outputStates() - @NotNull - public final java.util.List referenceStates() public final void setLockId(java.util.UUID) public final void setNotary(net.corda.core.identity.Party) @NotNull @@ -5920,7 +5150,6 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob public final void verify(net.corda.core.node.ServiceHub) @NotNull public final net.corda.core.transactions.TransactionBuilder withItems(Object...) - public static final net.corda.core.transactions.TransactionBuilder$Companion Companion ## @DoNotImplement public interface net.corda.core.transactions.TransactionWithSignatures extends net.corda.core.contracts.NamedByHash @@ -5955,16 +5184,12 @@ public abstract class net.corda.core.transactions.TraversableTransaction extends public net.corda.core.identity.Party getNotary() @NotNull public java.util.List> getOutputs() - @NotNull - public java.util.List getReferences() @Nullable public final net.corda.core.contracts.TimeWindow getTimeWindow() ## @DoNotImplement @CordaSerializable public final class net.corda.core.transactions.WireTransaction extends net.corda.core.transactions.TraversableTransaction - public (java.util.List) - public (java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow) public (java.util.List, java.util.List, java.util.List>, java.util.List>, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) public (java.util.List, net.corda.core.contracts.PrivacySalt) @NotNull @@ -6003,8 +5228,6 @@ public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Ob public int compareTo(net.corda.core.utilities.ByteSequence) @NotNull public final net.corda.core.utilities.ByteSequence copy() - @NotNull - public final byte[] copyBytes() public boolean equals(Object) @NotNull public abstract byte[] getBytes() @@ -6020,16 +5243,11 @@ public abstract class net.corda.core.utilities.ByteSequence extends java.lang.Ob @NotNull public final java.io.ByteArrayInputStream open() @NotNull - public final java.nio.ByteBuffer putTo(java.nio.ByteBuffer) - @NotNull - public final java.nio.ByteBuffer slice(int, int) - @NotNull public final net.corda.core.utilities.ByteSequence subSequence(int, int) @NotNull public final net.corda.core.utilities.ByteSequence take(int) @NotNull public String toString() - public final void writeTo(java.io.OutputStream) public static final net.corda.core.utilities.ByteSequence$Companion Companion ## public static final class net.corda.core.utilities.ByteSequence$Companion extends java.lang.Object @@ -6106,8 +5324,6 @@ public final class net.corda.core.utilities.KotlinUtilsKt extends java.lang.Obje public static final int exactAdd(int, int) public static final long exactAdd(long, long) @NotNull - public static final java.util.Map filterNotNullValues(java.util.Map) - @NotNull public static final java.time.Duration getDays(int) @NotNull public static final java.time.Duration getHours(int) @@ -6119,13 +5335,10 @@ public final class net.corda.core.utilities.KotlinUtilsKt extends java.lang.Obje @NotNull public static final java.time.Duration getSeconds(int) @NotNull - public static final java.util.List lazyMapped(java.util.List, kotlin.jvm.functions.Function2) - @NotNull public static final net.corda.core.utilities.NonEmptySet toNonEmptySet(java.util.Collection) public static final void trace(org.slf4j.Logger, kotlin.jvm.functions.Function0) @NotNull public static final net.corda.core.utilities.PropertyDelegate transient(kotlin.jvm.functions.Function0) - public static final synchronized void warnOnce(org.slf4j.Logger, String) ## @CordaSerializable public final class net.corda.core.utilities.NetworkHostAndPort extends java.lang.Object @@ -6251,7 +5464,6 @@ public final class net.corda.core.utilities.ProgressTracker extends java.lang.Ob public final net.corda.core.utilities.ProgressTracker$Step nextStep() public final void setChildProgressTracker(net.corda.core.utilities.ProgressTracker$Step, net.corda.core.utilities.ProgressTracker) public final void setCurrentStep(net.corda.core.utilities.ProgressTracker$Step) - public static final net.corda.core.utilities.ProgressTracker$Companion Companion ## @CordaSerializable public abstract static class net.corda.core.utilities.ProgressTracker$Change extends java.lang.Object @@ -6312,21 +5524,12 @@ public static final class net.corda.core.utilities.ProgressTracker$Change$Struct @NotNull public String toString() ## -public static final class net.corda.core.utilities.ProgressTracker$Companion extends java.lang.Object - @NotNull - public final kotlin.jvm.functions.Function0 getDEFAULT_TRACKER() -## @CordaSerializable public static final class net.corda.core.utilities.ProgressTracker$DONE extends net.corda.core.utilities.ProgressTracker$Step public boolean equals(Object) public static final net.corda.core.utilities.ProgressTracker$DONE INSTANCE ## @CordaSerializable -public static final class net.corda.core.utilities.ProgressTracker$STARTING extends net.corda.core.utilities.ProgressTracker$Step - public boolean equals(Object) - public static final net.corda.core.utilities.ProgressTracker$STARTING INSTANCE -## -@CordaSerializable public static class net.corda.core.utilities.ProgressTracker$Step extends java.lang.Object public (String) @Nullable @@ -6346,21 +5549,11 @@ public static final class net.corda.core.utilities.ProgressTracker$UNSTARTED ext public interface net.corda.core.utilities.PropertyDelegate public abstract T getValue(Object, kotlin.reflect.KProperty) ## -public final class net.corda.core.utilities.SgxSupport extends java.lang.Object - public static final boolean isInsideEnclave() - public static final net.corda.core.utilities.SgxSupport INSTANCE -## @CordaSerializable public abstract class net.corda.core.utilities.Try extends java.lang.Object @NotNull public final net.corda.core.utilities.Try combine(net.corda.core.utilities.Try, kotlin.jvm.functions.Function2) @NotNull - public final net.corda.core.utilities.Try doOnException(kotlin.jvm.functions.Function1) - @NotNull - public final net.corda.core.utilities.Try doOnFailure(kotlin.jvm.functions.Function1) - @NotNull - public final net.corda.core.utilities.Try doOnSuccess(kotlin.jvm.functions.Function1) - @NotNull public final net.corda.core.utilities.Try flatMap(kotlin.jvm.functions.Function1>) public abstract A getOrThrow() public abstract boolean isFailure() @@ -6439,8 +5632,6 @@ public final class net.corda.client.jackson.JacksonSupport extends java.lang.Obj @NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean) @NotNull - public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean, boolean) - @NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService) @NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory) @@ -6451,8 +5642,6 @@ public final class net.corda.client.jackson.JacksonSupport extends java.lang.Obj @NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper(com.fasterxml.jackson.core.JsonFactory) @NotNull - public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper(com.fasterxml.jackson.core.JsonFactory, boolean) - @NotNull public final com.fasterxml.jackson.databind.Module getCordaModule() public static final net.corda.client.jackson.JacksonSupport INSTANCE ## @@ -6483,16 +5672,11 @@ public static final class net.corda.client.jackson.JacksonSupport$CordaX500NameS public void serialize(net.corda.core.identity.CordaX500Name, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) public static final net.corda.client.jackson.JacksonSupport$CordaX500NameSerializer INSTANCE ## -@DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$IdentityObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper public (net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean) - public (net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean, boolean) public final boolean getFuzzyIdentityMatch() @NotNull public final net.corda.core.node.services.IdentityService getIdentityService() - public boolean isFullParties() - @Nullable - public net.corda.core.node.NodeInfo nodeInfoFromParty(net.corda.core.identity.AbstractParty) @NotNull public java.util.Set partiesFromName(String) @Nullable @@ -6503,10 +5687,6 @@ public static final class net.corda.client.jackson.JacksonSupport$IdentityObject @DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$NoPartyObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper public (com.fasterxml.jackson.core.JsonFactory) - public (com.fasterxml.jackson.core.JsonFactory, boolean) - public boolean isFullParties() - @Nullable - public net.corda.core.node.NodeInfo nodeInfoFromParty(net.corda.core.identity.AbstractParty) @NotNull public java.util.Set partiesFromName(String) @Nullable @@ -6537,11 +5717,7 @@ public static final class net.corda.client.jackson.JacksonSupport$PartyDeseriali public net.corda.core.identity.Party deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext) public static final net.corda.client.jackson.JacksonSupport$PartyDeserializer INSTANCE ## -@DoNotImplement public static interface net.corda.client.jackson.JacksonSupport$PartyObjectMapper - public abstract boolean isFullParties() - @Nullable - public abstract net.corda.core.node.NodeInfo nodeInfoFromParty(net.corda.core.identity.AbstractParty) @NotNull public abstract java.util.Set partiesFromName(String) @Nullable @@ -6562,16 +5738,11 @@ public static final class net.corda.client.jackson.JacksonSupport$PublicKeySeria public void serialize(java.security.PublicKey, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) public static final net.corda.client.jackson.JacksonSupport$PublicKeySerializer INSTANCE ## -@DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$RpcObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper public (net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean) - public (net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean, boolean) public final boolean getFuzzyIdentityMatch() @NotNull public final net.corda.core.messaging.CordaRPCOps getRpc() - public boolean isFullParties() - @Nullable - public net.corda.core.node.NodeInfo nodeInfoFromParty(net.corda.core.identity.AbstractParty) @NotNull public java.util.Set partiesFromName(String) @Nullable @@ -6713,8 +5884,6 @@ public interface net.corda.testing.driver.DriverDSL @NotNull public abstract net.corda.core.concurrent.CordaFuture startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String) @NotNull - public abstract net.corda.core.concurrent.CordaFuture startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, java.util.Collection, boolean, java.util.Map>, ? extends Class>>) - @NotNull public abstract net.corda.core.concurrent.CordaFuture startWebserver(net.corda.testing.driver.NodeHandle) @NotNull public abstract net.corda.core.concurrent.CordaFuture startWebserver(net.corda.testing.driver.NodeHandle, String) @@ -6722,11 +5891,8 @@ public interface net.corda.testing.driver.DriverDSL public final class net.corda.testing.driver.DriverParameters extends java.lang.Object public () public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Collection) public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, boolean) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, boolean, java.util.Collection, boolean) public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean) - public (boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean, java.util.Set) public final boolean component1() @NotNull public final java.util.List component10() @@ -6735,13 +5901,6 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O @NotNull public final net.corda.core.node.NetworkParameters component12() @NotNull - public final java.util.Map component13() - public final boolean component14() - public final boolean component15() - @Nullable - public final java.util.Collection component16() - public final boolean component17() - @NotNull public final java.nio.file.Path component2() @NotNull public final net.corda.testing.driver.PortAllocation component3() @@ -6756,33 +5915,18 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O public final java.util.List component9() @NotNull public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters) - @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map, boolean, boolean, java.util.Collection, boolean) - @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set) - @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean) - @NotNull - public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map, boolean, boolean, boolean, java.util.List, java.util.List, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, java.util.Set) public boolean equals(Object) - @Nullable - public final java.util.Collection getCordappsForAllNodes() @NotNull public final net.corda.testing.driver.PortAllocation getDebugPortAllocation() @NotNull public final java.nio.file.Path getDriverDirectory() - public final boolean getEnableSNI() @NotNull public final java.util.List getExtraCordappPackagesToScan() - public final boolean getInMemoryDB() - public final boolean getInitialiseSerialization() @NotNull public final net.corda.testing.driver.JmxPolicy getJmxPolicy() @NotNull public final net.corda.core.node.NetworkParameters getNetworkParameters() @NotNull - public final java.util.Map getNotaryCustomOverrides() - @NotNull public final java.util.List getNotarySpecs() @NotNull public final net.corda.testing.driver.PortAllocation getPortAllocation() @@ -6793,29 +5937,20 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O public final boolean getWaitForAllNodesToFinish() public int hashCode() public final boolean isDebug() - @NotNull public String toString() @NotNull - public final net.corda.testing.driver.DriverParameters withCordappsForAllNodes(java.util.Collection) - @NotNull public final net.corda.testing.driver.DriverParameters withDebugPortAllocation(net.corda.testing.driver.PortAllocation) @NotNull public final net.corda.testing.driver.DriverParameters withDriverDirectory(java.nio.file.Path) @NotNull public final net.corda.testing.driver.DriverParameters withExtraCordappPackagesToScan(java.util.List) @NotNull - public final net.corda.testing.driver.DriverParameters withInMemoryDB(boolean) - @NotNull - public final net.corda.testing.driver.DriverParameters withInitialiseSerialization(boolean) - @NotNull public final net.corda.testing.driver.DriverParameters withIsDebug(boolean) @NotNull public final net.corda.testing.driver.DriverParameters withJmxPolicy(net.corda.testing.driver.JmxPolicy) @NotNull public final net.corda.testing.driver.DriverParameters withNetworkParameters(net.corda.core.node.NetworkParameters) @NotNull - public final net.corda.testing.driver.DriverParameters withNotaryCustomOverrides(java.util.Map) - @NotNull public final net.corda.testing.driver.DriverParameters withNotarySpecs(java.util.List) @NotNull public final net.corda.testing.driver.DriverParameters withPortAllocation(net.corda.testing.driver.PortAllocation) @@ -6850,7 +5985,6 @@ public final class net.corda.testing.driver.JmxPolicy extends java.lang.Object public final net.corda.testing.driver.PortAllocation getJmxHttpServerPortAllocation() public final boolean getStartJmxHttpServer() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -6866,22 +6000,15 @@ public interface net.corda.testing.driver.NodeHandle extends java.lang.AutoClose @NotNull public abstract net.corda.core.utilities.NetworkHostAndPort getRpcAddress() @NotNull - public abstract net.corda.core.utilities.NetworkHostAndPort getRpcAdminAddress() - @NotNull public abstract java.util.List getRpcUsers() public abstract void stop() ## public final class net.corda.testing.driver.NodeParameters extends java.lang.Object public () public (net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String) - public (net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, String) - public (net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, String, java.util.Collection, boolean, java.util.Map>, ? extends Class>>) - public (net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, java.util.Set, boolean) @Nullable public final net.corda.core.identity.CordaX500Name component1() @NotNull - public final java.util.Map>, Class>> component10() - @NotNull public final java.util.List component2() @NotNull public final net.corda.testing.driver.VerifierType component3() @@ -6891,31 +6018,15 @@ public final class net.corda.testing.driver.NodeParameters extends java.lang.Obj public final Boolean component5() @NotNull public final String component6() - @Nullable - public final String component7() - @NotNull - public final java.util.Collection component8() - public final boolean component9() @NotNull public final net.corda.testing.driver.NodeParameters copy(net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String) - @NotNull - public final net.corda.testing.driver.NodeParameters copy(net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, String) - @NotNull - public final net.corda.testing.driver.NodeParameters copy(net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, String, java.util.Collection, boolean, java.util.Map>, ? extends Class>>) public boolean equals(Object) @NotNull - public final java.util.Collection getAdditionalCordapps() - @NotNull public final java.util.Map getCustomOverrides() @NotNull - public final java.util.Map>, Class>> getFlowOverrides() - @Nullable - public final String getLogLevel() - @NotNull public final String getMaximumHeapSize() @Nullable public final net.corda.core.identity.CordaX500Name getProvidedName() - public final boolean getRegenerateCordappsOnStart() @NotNull public final java.util.List getRpcUsers() @Nullable @@ -6923,17 +6034,10 @@ public final class net.corda.testing.driver.NodeParameters extends java.lang.Obj @NotNull public final net.corda.testing.driver.VerifierType getVerifierType() public int hashCode() - @NotNull public String toString() @NotNull - public final net.corda.testing.driver.NodeParameters withAdditionalCordapps(java.util.Set) - @NotNull public final net.corda.testing.driver.NodeParameters withCustomOverrides(java.util.Map) @NotNull - public final net.corda.testing.driver.NodeParameters withDeleteExistingCordappsDirectory(boolean) - @NotNull - public final net.corda.testing.driver.NodeParameters withLogLevel(String) - @NotNull public final net.corda.testing.driver.NodeParameters withMaximumHeapSize(String) @NotNull public final net.corda.testing.driver.NodeParameters withProvidedName(net.corda.core.identity.CordaX500Name) @@ -6960,7 +6064,6 @@ public final class net.corda.testing.driver.NotaryHandle extends java.lang.Objec public final net.corda.core.concurrent.CordaFuture> getNodeHandles() public final boolean getValidating() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -7001,7 +6104,6 @@ public final class net.corda.testing.driver.WebserverHandle extends java.lang.Ob @NotNull public final Process getProcess() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -7018,7 +6120,6 @@ public static final class net.corda.testing.node.ClusterSpec$Raft extends net.co public boolean equals(Object) public int getClusterSize() public int hashCode() - @NotNull public String toString() ## @ThreadSafe @@ -7108,51 +6209,10 @@ public static final class net.corda.testing.node.InMemoryMessagingNetwork$Servic public () public A pickNext(net.corda.testing.node.InMemoryMessagingNetwork$DistributedServiceHandle, java.util.List) ## -public final class net.corda.testing.node.MockNetFlowTimeOut extends java.lang.Object - public (java.time.Duration, int, double) - @NotNull - public final java.time.Duration component1() - public final int component2() - public final double component3() - @NotNull - public final net.corda.testing.node.MockNetFlowTimeOut copy(java.time.Duration, int, double) - public boolean equals(Object) - public final double getBackoffBase() - public final int getMaxRestartCount() - @NotNull - public final java.time.Duration getTimeout() - public int hashCode() - @NotNull - public String toString() -## -public final class net.corda.testing.node.MockNetNotaryConfig extends java.lang.Object - public (boolean, com.typesafe.config.Config, String, net.corda.core.identity.CordaX500Name) - public final boolean component1() - @Nullable - public final com.typesafe.config.Config component2() - @Nullable - public final String component3() - @Nullable - public final net.corda.core.identity.CordaX500Name component4() - @NotNull - public final net.corda.testing.node.MockNetNotaryConfig copy(boolean, com.typesafe.config.Config, String, net.corda.core.identity.CordaX500Name) - public boolean equals(Object) - @Nullable - public final String getClassName() - @Nullable - public final com.typesafe.config.Config getExtraConfig() - @Nullable - public final net.corda.core.identity.CordaX500Name getServiceLegalName() - public final boolean getValidating() - public int hashCode() - @NotNull - public String toString() -## public class net.corda.testing.node.MockNetwork extends java.lang.Object public (java.util.List) public (java.util.List, net.corda.testing.node.MockNetworkParameters) public (java.util.List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, java.util.List, net.corda.core.node.NetworkParameters) - public (java.util.List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, java.util.List, net.corda.core.node.NetworkParameters, java.util.Collection) @NotNull public final java.nio.file.Path baseDirectory(int) @NotNull @@ -7164,12 +6224,6 @@ 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) @NotNull - public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides) - @NotNull - public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.Collection) - @NotNull - public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.List) - @NotNull public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters) @NotNull public final net.corda.testing.node.StartedMockNode createPartyNode(net.corda.core.identity.CordaX500Name) @@ -7182,18 +6236,10 @@ 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) @NotNull - public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides) - @NotNull - public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.Collection) - @NotNull - public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.List) - @NotNull public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters) @NotNull public final java.util.List getCordappPackages() @NotNull - public final java.util.Collection getCordappsForAllNodes() - @NotNull public final net.corda.core.identity.Party getDefaultNotaryIdentity() @NotNull public final net.corda.testing.node.StartedMockNode getDefaultNotaryNode() @@ -7216,8 +6262,6 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object public final void stopNodes() public final void waitQuiescent() ## -public final class net.corda.testing.node.MockNetworkKt extends java.lang.Object -## public final class net.corda.testing.node.MockNetworkNotarySpec extends java.lang.Object public (net.corda.core.identity.CordaX500Name) public (net.corda.core.identity.CordaX500Name, boolean) @@ -7231,7 +6275,6 @@ public final class net.corda.testing.node.MockNetworkNotarySpec extends java.lan public final net.corda.core.identity.CordaX500Name getName() public final boolean getValidating() public int hashCode() - @NotNull public String toString() ## public final class net.corda.testing.node.MockNetworkParameters extends java.lang.Object @@ -7257,7 +6300,6 @@ public final class net.corda.testing.node.MockNetworkParameters extends java.lan public final net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy getServicePeerAllocationStrategy() public final boolean getThreadPerNode() public int hashCode() - @NotNull public String toString() @NotNull public final net.corda.testing.node.MockNetworkParameters withNetworkParameters(net.corda.core.node.NetworkParameters) @@ -7270,65 +6312,24 @@ public final class net.corda.testing.node.MockNetworkParameters extends java.lan @NotNull public final net.corda.testing.node.MockNetworkParameters withThreadPerNode(boolean) ## -public final class net.corda.testing.node.MockNodeConfigOverrides extends java.lang.Object - public () - public (java.util.Map, net.corda.testing.node.MockNetNotaryConfig, net.corda.testing.node.MockNetFlowTimeOut) - @Nullable - public final java.util.Map component1() - @Nullable - public final net.corda.testing.node.MockNetNotaryConfig component2() - @Nullable - public final net.corda.testing.node.MockNetFlowTimeOut component3() - @NotNull - public final net.corda.testing.node.MockNodeConfigOverrides copy(java.util.Map, net.corda.testing.node.MockNetNotaryConfig, net.corda.testing.node.MockNetFlowTimeOut) - public boolean equals(Object) - @Nullable - public final java.util.Map getExtraDataSourceProperties() - @Nullable - public final net.corda.testing.node.MockNetFlowTimeOut getFlowTimeout() - @Nullable - public final net.corda.testing.node.MockNetNotaryConfig getNotary() - public int hashCode() - @NotNull - public String toString() -## public final class net.corda.testing.node.MockNodeParameters extends java.lang.Object public () - public (Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides) - public (Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.Collection) @Nullable public final Integer component1() @Nullable public final net.corda.core.identity.CordaX500Name component2() @NotNull public final java.math.BigInteger component3() - @Nullable - public final net.corda.testing.node.MockNodeConfigOverrides component4() - @NotNull - public final java.util.Collection component5() - @NotNull - public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides) - @NotNull - public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, net.corda.testing.node.MockNodeConfigOverrides, java.util.Collection) public boolean equals(Object) @NotNull - public final java.util.Collection getAdditionalCordapps() - @Nullable - public final net.corda.testing.node.MockNodeConfigOverrides getConfigOverrides() - @NotNull public final java.math.BigInteger getEntropyRoot() @Nullable public final Integer getForcedID() @Nullable public final net.corda.core.identity.CordaX500Name getLegalName() public int hashCode() - @NotNull public String toString() @NotNull - public final net.corda.testing.node.MockNodeParameters withAdditionalCordapps(java.util.Collection) - @NotNull - public final net.corda.testing.node.MockNodeParameters withConfigOverrides(net.corda.testing.node.MockNodeConfigOverrides) - @NotNull public final net.corda.testing.node.MockNodeParameters withEntropyRoot(java.math.BigInteger) @NotNull public final net.corda.testing.node.MockNodeParameters withForcedID(Integer) @@ -7345,7 +6346,6 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem public (Iterable, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, net.corda.core.node.NetworkParameters, java.security.KeyPair...) public (Iterable, net.corda.testing.core.TestIdentity, net.corda.core.node.services.IdentityService, java.security.KeyPair...) public (Iterable, net.corda.testing.core.TestIdentity, java.security.KeyPair...) - public (java.util.List, net.corda.testing.core.TestIdentity, net.corda.testing.core.TestIdentity...) public (net.corda.core.identity.CordaX500Name) public (net.corda.core.identity.CordaX500Name, java.security.KeyPair, java.security.KeyPair...) public (net.corda.core.identity.CordaX500Name, net.corda.core.node.services.IdentityService) @@ -7367,8 +6367,6 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem @NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @NotNull - public net.corda.core.cordapp.CordappContext getAppContext() - @NotNull public final net.corda.testing.services.MockAttachmentStorage getAttachments() @NotNull public net.corda.testing.node.TestClock getClock() @@ -7421,9 +6419,6 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem public net.corda.core.transactions.SignedTransaction signInitialTransaction(net.corda.core.transactions.TransactionBuilder, java.security.PublicKey) @NotNull public net.corda.core.contracts.StateAndRef toStateAndRef(net.corda.core.contracts.StateRef) - public void withEntityManager(java.util.function.Consumer) - @NotNull - public T withEntityManager(kotlin.jvm.functions.Function1) public static final net.corda.testing.node.MockServices$Companion Companion ## public static final class net.corda.testing.node.MockServices$Companion extends java.lang.Object @@ -7478,7 +6473,6 @@ public final class net.corda.testing.node.NotarySpec extends java.lang.Object @NotNull public final net.corda.testing.driver.VerifierType getVerifierType() public int hashCode() - @NotNull public String toString() ## public interface net.corda.testing.node.ResponderFlowFactory @@ -7498,8 +6492,6 @@ public final class net.corda.testing.node.StartedMockNode extends java.lang.Obje @NotNull public final rx.Observable registerInitiatedFlow(Class) @NotNull - public final net.corda.core.concurrent.CordaFuture registerResponderFlow(Class>, net.corda.testing.node.ResponderFlowFactory, Class) - @NotNull public final net.corda.core.concurrent.CordaFuture startFlow(net.corda.core.flows.FlowLogic) public final void stop() public final T transaction(kotlin.jvm.functions.Function0) @@ -7513,54 +6505,9 @@ public final class net.corda.testing.node.TestClock extends net.corda.node.Mutab public final synchronized void advanceBy(java.time.Duration) public final synchronized void setTo(java.time.Instant) ## -@DoNotImplement -public interface net.corda.testing.node.TestCordapp - @NotNull - public abstract java.util.Map getConfig() - @NotNull - public abstract String getName() - @NotNull - public abstract java.util.Set getPackages() - public abstract int getTargetVersion() - @NotNull - public abstract String getTitle() - @NotNull - public abstract String getVendor() - @NotNull - public abstract String getVersion() - @NotNull - public abstract net.corda.testing.node.TestCordapp withConfig(java.util.Map) - @NotNull - public abstract net.corda.testing.node.TestCordapp withName(String) - @NotNull - public abstract net.corda.testing.node.TestCordapp withTargetVersion(int) - @NotNull - public abstract net.corda.testing.node.TestCordapp withTitle(String) - @NotNull - public abstract net.corda.testing.node.TestCordapp withVendor(String) - @NotNull - public abstract net.corda.testing.node.TestCordapp withVersion(String) -## -public static final class net.corda.testing.node.TestCordapp$Factory extends java.lang.Object - public () - @NotNull - public static final net.corda.testing.node.TestCordapp fromPackages(java.util.Collection) - @NotNull - public static final net.corda.testing.node.TestCordapp fromPackages(String...) - public static final net.corda.testing.node.TestCordapp$Factory$Companion Companion -## -public static final class net.corda.testing.node.TestCordapp$Factory$Companion extends java.lang.Object - @NotNull - public final net.corda.testing.node.TestCordapp fromPackages(java.util.Collection) - @NotNull - public final net.corda.testing.node.TestCordapp fromPackages(String...) -## public final class net.corda.testing.node.UnstartedMockNode extends java.lang.Object public final int getId() @NotNull - public final net.corda.testing.node.StartedMockNode getStarted() - public final boolean isStarted() - @NotNull public final net.corda.testing.node.StartedMockNode start() public static final net.corda.testing.node.UnstartedMockNode$Companion Companion ## @@ -7584,13 +6531,8 @@ public final class net.corda.testing.node.User extends java.lang.Object @NotNull public final String getUsername() public int hashCode() - @NotNull public String toString() ## -public class net.corda.client.rpc.ConnectionFailureException extends net.corda.client.rpc.RPCException - public () - public (Throwable) -## public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object public (java.util.List) public (java.util.List, net.corda.client.rpc.CordaRPCClientConfiguration) @@ -7600,10 +6542,6 @@ public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object public final net.corda.client.rpc.CordaRPCConnection start(String, String) @NotNull public final net.corda.client.rpc.CordaRPCConnection start(String, String, net.corda.core.context.Trace, net.corda.core.context.Actor) - @NotNull - public final net.corda.client.rpc.CordaRPCConnection start(String, String, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.identity.CordaX500Name) - @NotNull - public final net.corda.client.rpc.CordaRPCConnection start(String, String, net.corda.core.identity.CordaX500Name) public final A use(String, String, kotlin.jvm.functions.Function1) public static final net.corda.client.rpc.CordaRPCClient$Companion Companion ## @@ -7614,62 +6552,15 @@ public static final class net.corda.client.rpc.CordaRPCClient$Companion extends public final net.corda.client.rpc.CordaRPCClient createWithSsl(net.corda.core.utilities.NetworkHostAndPort, net.corda.core.messaging.ClientRpcSslOptions, net.corda.client.rpc.CordaRPCClientConfiguration) ## public class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.Object - public () public (java.time.Duration) - public (java.time.Duration, int) - public (java.time.Duration, int, boolean) - public (java.time.Duration, int, boolean, java.time.Duration) - public (java.time.Duration, int, boolean, java.time.Duration, int) - public (java.time.Duration, int, boolean, java.time.Duration, int, int) - public (java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration) - public (java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double) - public (java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int) - public (java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int) - public (java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration) @NotNull public final java.time.Duration component1() @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy() - @NotNull public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int) - @NotNull - public final net.corda.client.rpc.CordaRPCClientConfiguration copy(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration) public boolean equals(Object) - public int getCacheConcurrencyLevel() @NotNull public java.time.Duration getConnectionMaxRetryInterval() - @NotNull - public java.time.Duration getConnectionRetryInterval() - public double getConnectionRetryIntervalMultiplier() - @NotNull - public java.time.Duration getDeduplicationCacheExpiry() - public int getMaxFileSize() - public int getMaxReconnectAttempts() - public int getMinimumServerProtocolVersion() - public int getObservationExecutorPoolSize() - @NotNull - public java.time.Duration getReapInterval() - public boolean getTrackRpcCallSites() public int hashCode() - @NotNull public String toString() public static final net.corda.client.rpc.CordaRPCClientConfiguration$Companion Companion @NotNull @@ -7727,7 +6618,6 @@ public final class net.corda.testing.contracts.DummyContract extends java.lang.O public static final net.corda.core.transactions.TransactionBuilder move(java.util.List>, net.corda.core.identity.AbstractParty) @NotNull public static final net.corda.core.transactions.TransactionBuilder move(net.corda.core.contracts.StateAndRef, net.corda.core.identity.AbstractParty) - @NotNull public String toString() public void verify(net.corda.core.transactions.LedgerTransaction) public static final net.corda.testing.contracts.DummyContract$Companion Companion @@ -7765,7 +6655,6 @@ public static final class net.corda.testing.contracts.DummyContract$MultiOwnerSt @NotNull public java.util.List getParticipants() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -7783,7 +6672,6 @@ public static final class net.corda.testing.contracts.DummyContract$SingleOwnerS @NotNull public java.util.List getParticipants() public int hashCode() - @NotNull public String toString() @NotNull public net.corda.core.contracts.CommandAndState withNewOwner(net.corda.core.identity.AbstractParty) @@ -7829,67 +6717,19 @@ public static final class net.corda.testing.contracts.DummyContractV2$State exte @NotNull public java.util.List getParticipants() public int hashCode() - @NotNull public String toString() ## -public final class net.corda.testing.contracts.DummyContractV3 extends java.lang.Object implements net.corda.core.contracts.UpgradedContractWithLegacyConstraint - public () - @NotNull - public String getLegacyContract() - @NotNull - public net.corda.core.contracts.AttachmentConstraint getLegacyContractConstraint() - @NotNull - public net.corda.testing.contracts.DummyContractV3$State upgrade(net.corda.testing.contracts.DummyContractV2$State) - public void verify(net.corda.core.transactions.LedgerTransaction) - public static final net.corda.testing.contracts.DummyContractV3$Companion Companion - @NotNull - public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContractV3" -## -public static interface net.corda.testing.contracts.DummyContractV3$Commands extends net.corda.core.contracts.CommandData -## -public static final class net.corda.testing.contracts.DummyContractV3$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV3$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV3$Companion extends java.lang.Object -## -public static final class net.corda.testing.contracts.DummyContractV3$State extends java.lang.Object implements net.corda.core.contracts.ContractState - public (int, java.util.List) - public final int component1() - @NotNull - public final java.util.List component2() - @NotNull - public final net.corda.testing.contracts.DummyContractV3$State copy(int, java.util.List) - public boolean equals(Object) - public final int getMagicNumber() - @NotNull - public final java.util.List getOwners() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() -## -@BelongsToContract public final class net.corda.testing.contracts.DummyState extends java.lang.Object implements net.corda.core.contracts.ContractState public () public (int) - public (int, java.util.List) public final int component1() @NotNull - public final java.util.List component2() - @NotNull public final net.corda.testing.contracts.DummyState copy(int) - @NotNull - public final net.corda.testing.contracts.DummyState copy(int, java.util.List) public boolean equals(Object) public final int getMagicNumber() @NotNull public java.util.List getParticipants() public int hashCode() - @NotNull public String toString() ## public final class net.corda.testing.core.DummyCommandData extends net.corda.core.contracts.TypeOnlyCommandData @@ -7913,7 +6753,6 @@ public final class net.corda.testing.core.Expect extends java.lang.Object @NotNull public final kotlin.jvm.functions.Function1 getMatch() public int hashCode() - @NotNull public String toString() ## @DoNotImplement @@ -7997,20 +6836,6 @@ public final class net.corda.testing.core.ExpectKt extends java.lang.Object @NotNull public static final net.corda.testing.core.ExpectCompose sequence(net.corda.testing.core.ExpectCompose...) ## -public final class net.corda.testing.core.JarSignatureTestUtils extends java.lang.Object - public final void addIndexList(java.nio.file.Path, String) - public final void createJar(java.nio.file.Path, String, String...) - public final void executeProcess(java.nio.file.Path, String...) - public final void generateKey(java.nio.file.Path, String, String, String, String, String, String) - @NotNull - public final java.nio.file.Path getBin() - @NotNull - public final java.util.List getJarSigners(java.nio.file.Path, String) - @NotNull - public final java.security.PublicKey signJar(java.nio.file.Path, String, String, String, String) - public final void updateJar(java.nio.file.Path, String, String...) - public static final net.corda.testing.core.JarSignatureTestUtils INSTANCE -## public final class net.corda.testing.core.SerializationEnvironmentRule extends java.lang.Object implements org.junit.rules.TestRule public () public (boolean) @@ -8047,13 +6872,7 @@ public final class net.corda.testing.core.TestConstants extends java.lang.Object public final class net.corda.testing.core.TestIdentity extends java.lang.Object public (net.corda.core.identity.CordaX500Name) public (net.corda.core.identity.CordaX500Name, long) - public (net.corda.core.identity.CordaX500Name, long, net.corda.core.crypto.SignatureScheme) public (net.corda.core.identity.CordaX500Name, java.security.KeyPair) - public (net.corda.core.identity.CordaX500Name, net.corda.core.crypto.SignatureScheme) - @NotNull - public static final net.corda.testing.core.TestIdentity fresh(String) - @NotNull - public static final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) @NotNull public final net.corda.core.identity.PartyAndCertificate getIdentity() @NotNull @@ -8071,8 +6890,6 @@ public final class net.corda.testing.core.TestIdentity extends java.lang.Object public static final class net.corda.testing.core.TestIdentity$Companion extends java.lang.Object @NotNull public final net.corda.testing.core.TestIdentity fresh(String) - @NotNull - public final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) ## public final class net.corda.testing.core.TestUtils extends java.lang.Object @NotNull @@ -8087,41 +6904,10 @@ public final class net.corda.testing.core.TestUtils extends java.lang.Object @NotNull public static final net.corda.core.identity.PartyAndCertificate getTestPartyAndCertificate(net.corda.core.identity.Party) @NotNull - public static final net.corda.core.identity.CordaX500Name makeUnique(net.corda.core.identity.CordaX500Name) - @NotNull - public static final java.util.Collection product(java.util.Collection, java.util.Collection) - @NotNull public static final net.corda.core.identity.Party singleIdentity(net.corda.core.node.NodeInfo) @NotNull public static final net.corda.core.identity.PartyAndCertificate singleIdentityAndCert(net.corda.core.node.NodeInfo) ## -public final class net.corda.testing.database.DatabaseConstants extends java.lang.Object - @NotNull - public static final String DATA_SOURCE_CLASSNAME = "dataSourceProperties.dataSourceClassName" - @NotNull - public static final String DATA_SOURCE_PASSWORD = "dataSourceProperties.dataSource.password" - @NotNull - public static final String DATA_SOURCE_URL = "dataSourceProperties.dataSource.url" - @NotNull - public static final String DATA_SOURCE_USER = "dataSourceProperties.dataSource.user" - public static final net.corda.testing.database.DatabaseConstants INSTANCE - @NotNull - public static final String SCHEMA = "database.schema" - @NotNull - public static final String TRANSACTION_ISOLATION_LEVEL = "database.transactionIsolationLevel" -## -public final class net.corda.testing.database.DbScriptRunner extends java.lang.Object - @NotNull - public final java.util.List merge(java.util.List, String) - @NotNull - public final java.util.List merge(java.util.List, java.util.List) - public final void runDbScript(String, String, java.util.List) - public static final net.corda.testing.database.DbScriptRunner INSTANCE -## -public final class net.corda.testing.database.ListPopulator extends java.lang.Object implements org.springframework.jdbc.datasource.init.DatabasePopulator - public (boolean, boolean, java.util.List) - public void populate(java.sql.Connection) -## public final class net.corda.testing.dsl.AttachmentResolutionException extends net.corda.core.flows.FlowException public (net.corda.core.crypto.SecureHash) ## @@ -8225,7 +7011,6 @@ public final class net.corda.testing.dsl.TestLedgerDSLInterpreter extends java.l public final String outputToLabel(net.corda.core.contracts.ContractState) @NotNull public net.corda.core.contracts.StateAndRef retrieveOutputStateAndRef(Class, String) - @NotNull public String toString() @Nullable public final String transactionName(net.corda.core.crypto.SecureHash) @@ -8259,14 +7044,12 @@ public static final class net.corda.testing.dsl.TestLedgerDSLInterpreter$WireTra @NotNull public final net.corda.core.transactions.WireTransaction getTransaction() public int hashCode() - @NotNull public String toString() ## @DoNotImplement public final class net.corda.testing.dsl.TestTransactionDSLInterpreter extends java.lang.Object implements net.corda.testing.dsl.TransactionDSLInterpreter, net.corda.testing.dsl.OutputStateLookup public (net.corda.testing.dsl.TestLedgerDSLInterpreter, net.corda.core.transactions.TransactionBuilder) public void _attachment(String) - public void _attachment(String, net.corda.core.crypto.SecureHash, java.util.List) @NotNull public net.corda.testing.dsl.EnforceVerifyOrFail _tweak(kotlin.jvm.functions.Function1) public void attachment(net.corda.core.crypto.SecureHash) @@ -8293,11 +7076,9 @@ public final class net.corda.testing.dsl.TestTransactionDSLInterpreter extends j public int hashCode() public void input(net.corda.core.contracts.StateRef) public void output(String, String, net.corda.core.identity.Party, Integer, net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractState) - public void reference(net.corda.core.contracts.StateRef) @NotNull public net.corda.core.contracts.StateAndRef retrieveOutputStateAndRef(Class, String) public void timeWindow(net.corda.core.contracts.TimeWindow) - @NotNull public String toString() @NotNull public net.corda.testing.dsl.EnforceVerifyOrFail verifies() @@ -8306,12 +7087,9 @@ public final class net.corda.testing.dsl.TestTransactionDSLInterpreter extends j public final class net.corda.testing.dsl.TransactionDSL extends java.lang.Object implements net.corda.testing.dsl.TransactionDSLInterpreter public (T, net.corda.core.identity.Party) public void _attachment(String) - public void _attachment(String, net.corda.core.crypto.SecureHash, java.util.List) @NotNull public net.corda.testing.dsl.EnforceVerifyOrFail _tweak(kotlin.jvm.functions.Function1) public final void attachment(String) - public final void attachment(String, net.corda.core.crypto.SecureHash) - public final void attachment(String, net.corda.core.crypto.SecureHash, java.util.List) public void attachment(net.corda.core.crypto.SecureHash) public final void attachments(String...) public final void command(java.security.PublicKey, net.corda.core.contracts.CommandData) @@ -8325,7 +7103,6 @@ public final class net.corda.testing.dsl.TransactionDSL extends java.lang.Object @NotNull public net.corda.testing.dsl.LedgerDSLInterpreter getLedgerInterpreter() public final void input(String) - public final void input(String, String) public final void input(String, net.corda.core.contracts.ContractState) public void input(net.corda.core.contracts.StateRef) public final void output(String, int, net.corda.core.contracts.ContractState) @@ -8335,9 +7112,6 @@ public final class net.corda.testing.dsl.TransactionDSL extends java.lang.Object public final void output(String, String, net.corda.core.identity.Party, net.corda.core.contracts.ContractState) public final void output(String, net.corda.core.contracts.ContractState) public final void output(String, net.corda.core.identity.Party, net.corda.core.contracts.ContractState) - public final void reference(String) - public final void reference(String, net.corda.core.contracts.ContractState) - public void reference(net.corda.core.contracts.StateRef) @NotNull public net.corda.core.contracts.StateAndRef retrieveOutputStateAndRef(Class, String) public final void timeWindow(java.time.Instant) @@ -8351,7 +7125,6 @@ public final class net.corda.testing.dsl.TransactionDSL extends java.lang.Object @DoNotImplement public interface net.corda.testing.dsl.TransactionDSLInterpreter extends net.corda.testing.dsl.OutputStateLookup, net.corda.testing.dsl.Verifies public abstract void _attachment(String) - public abstract void _attachment(String, net.corda.core.crypto.SecureHash, java.util.List) @NotNull public abstract net.corda.testing.dsl.EnforceVerifyOrFail _tweak(kotlin.jvm.functions.Function1) public abstract void attachment(net.corda.core.crypto.SecureHash) @@ -8360,7 +7133,6 @@ public interface net.corda.testing.dsl.TransactionDSLInterpreter extends net.cor public abstract net.corda.testing.dsl.LedgerDSLInterpreter getLedgerInterpreter() public abstract void input(net.corda.core.contracts.StateRef) public abstract void output(String, String, net.corda.core.identity.Party, Integer, net.corda.core.contracts.AttachmentConstraint, net.corda.core.contracts.ContractState) - public abstract void reference(net.corda.core.contracts.StateRef) public abstract void timeWindow(net.corda.core.contracts.TimeWindow) ## @DoNotImplement @@ -8411,10 +7183,6 @@ public final class net.corda.testing.services.MockAttachmentStorage extends net. @NotNull public final net.corda.core.crypto.SecureHash importContractAttachment(java.util.List, String, java.io.InputStream) @NotNull - public final net.corda.core.crypto.SecureHash importContractAttachment(java.util.List, String, java.io.InputStream, net.corda.core.crypto.SecureHash) - @NotNull - public final net.corda.core.crypto.SecureHash importContractAttachment(java.util.List, String, java.io.InputStream, net.corda.core.crypto.SecureHash, java.util.List) - @NotNull public net.corda.core.crypto.SecureHash importOrGetAttachment(java.io.InputStream) @Nullable public net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash) diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt index 6eef15668a..2a2a11041d 100644 --- a/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt +++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/internal/CordaModule.kt @@ -3,7 +3,7 @@ package net.corda.client.jackson.internal import com.fasterxml.jackson.annotation.* -import com.fasterxml.jackson.annotation.JsonCreator.Mode.* +import com.fasterxml.jackson.annotation.JsonCreator.Mode.DISABLED import com.fasterxml.jackson.annotation.JsonInclude.Include import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParseException @@ -38,7 +38,10 @@ import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.parseAsHex import net.corda.core.utilities.toHexString import net.corda.serialization.internal.AllWhitelist -import net.corda.serialization.internal.amqp.* +import net.corda.serialization.internal.amqp.SerializerFactoryBuilder +import net.corda.serialization.internal.amqp.constructorForDeserialization +import net.corda.serialization.internal.amqp.hasCordaSerializable +import net.corda.serialization.internal.amqp.propertiesForSerialization import java.math.BigDecimal import java.security.PublicKey import java.security.cert.CertPath @@ -327,11 +330,11 @@ private class PartialTreeJson(val includedLeaf: SecureHash? = null, val right: PartialTreeJson? = null) { init { if (includedLeaf != null) { - require(leaf == null && left == null && right == null) + require(leaf == null && left == null && right == null) { "Invalid JSON structure" } } else if (leaf != null) { - require(left == null && right == null) + require(left == null && right == null) { "Invalid JSON structure" } } else { - require(left != null && right != null) + require(left != null && right != null) { "Invalid JSON structure" } } } } diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt index b42deddb8c..5bd5f65b5d 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt @@ -202,7 +202,7 @@ class NodeMonitorModel : AutoCloseable { val _connection = client.start(username, password) // Check connection is truly operational before returning it. val nodeInfo = _connection.proxy.nodeInfo() - require(nodeInfo.legalIdentitiesAndCerts.isNotEmpty()) + require(nodeInfo.legalIdentitiesAndCerts.isNotEmpty()){"No identity certificates found"} _connection } catch (exception: Exception) { if (shouldRetry) { diff --git a/common/validation/src/main/kotlin/net/corda/common/validation/internal/Validated.kt b/common/validation/src/main/kotlin/net/corda/common/validation/internal/Validated.kt index a93f4b44fb..9a21285409 100644 --- a/common/validation/src/main/kotlin/net/corda/common/validation/internal/Validated.kt +++ b/common/validation/src/main/kotlin/net/corda/common/validation/internal/Validated.kt @@ -126,7 +126,7 @@ interface Validated { */ class Unsuccessful(override val errors: Set) : Result(), Validated { init { - require(errors.isNotEmpty()) + require(errors.isNotEmpty()) { "No errors encountered during validation" } } override fun value(exceptionOnErrors: (Set) -> Exception) = throw exceptionOnErrors.invoke(errors) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt index be28292ff1..64f64c3f75 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt @@ -40,14 +40,14 @@ class CompositeKey private constructor(val threshold: Int, children: List - require(childAsn1 is ASN1Sequence) + require(childAsn1 is ASN1Sequence) { "Child key is not in ASN1 format" } val childSeq = childAsn1 as ASN1Sequence val key = Crypto.decodePublicKey((childSeq.getObjectAt(0) as DERBitString).bytes) val weight = ASN1Integer.getInstance(childSeq.getObjectAt(1)) @@ -274,7 +274,7 @@ class CompositeKey private constructor(val threshold: Int, children: List 0) + require(threshold == null || threshold > 0) { "Threshold must not be specified or its value must be greater than zero" } val n = children.size return when { n > 1 -> CompositeKey(threshold ?: children.map { (_, weight) -> weight }.sum(), children) diff --git a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt index 128bfa75a0..423125c70b 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt @@ -21,7 +21,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { /** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes). */ class SHA256(bytes: ByteArray) : SecureHash(bytes) { init { - require(bytes.size == 32) + require(bytes.size == 32) { "Invalid hash size, must be 32 bytes" } } } diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt index bd45e7490b..0523b00cba 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt @@ -45,7 +45,7 @@ val cordaBouncyCastleProvider = BouncyCastleProvider().apply { Security.addProvider(it) } val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply { - require(name == "BCPQC") // The constant it comes from is not final. + require(name == "BCPQC") { "Invalid PQCProvider name" } }.also { Security.addProvider(it) } diff --git a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt index c509abdd4f..5b27d28ef5 100644 --- a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt +++ b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt @@ -27,6 +27,11 @@ fun isUploaderTrusted(uploader: String?): Boolean = uploader in TRUSTED_UPLOADER @KeepForDJVM abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment { companion object { + /** + * Returns a function that knows how to load an attachment. + * + * TODO - this code together with the rest of the Attachment handling (including [FetchedAttachment]) needs some refactoring as it is really hard to follow. + */ @DeleteForDJVM fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray { return { diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 6595851d22..5c07f82117 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -1,6 +1,10 @@ package net.corda.core.internal import net.corda.core.DeleteForDJVM +import net.corda.core.KeepForDJVM +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.TransactionState import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.CordappConfig import net.corda.core.cordapp.CordappContext @@ -8,11 +12,14 @@ import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.node.ServicesForResolution import net.corda.core.node.ZoneVersionTooLowException +import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializedBytes import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction +import net.corda.core.utilities.OpaqueBytes import org.slf4j.MDC // *Internal* Corda-specific utilities @@ -73,3 +80,11 @@ class LazyMappedList(val originalList: List, val transform: (T, Int) -> override fun get(index: Int) = partialResolvedList[index] ?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed } } + +/** + * A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef). + * The [serializedState] is the actual component from the original transaction. + */ +@KeepForDJVM +@CordaSerializable +data class SerializedStateAndRef(val serializedState: SerializedBytes>, val ref: StateRef) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 9814502433..1a0a1d5143 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -5,7 +5,10 @@ package net.corda.core.internal import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.crypto.* -import net.corda.core.serialization.* +import net.corda.core.serialization.SerializationDefaults +import net.corda.core.serialization.SerializedBytes +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.UntrustworthyData import org.slf4j.Logger @@ -109,7 +112,7 @@ fun List.randomOrNull(): T? { /** Returns the index of the given item or throws [IllegalArgumentException] if not found. */ fun List.indexOfOrThrow(item: T): Int { val i = indexOf(item) - require(i != -1) + require(i != -1){"No such element"} return i } @@ -218,7 +221,8 @@ data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHa * Note that a slightly bigger than numOfExpectedBytes size is expected. */ @DeleteForDJVM - fun createInMemoryTestZip(numOfExpectedBytes: Int, content: Byte): InputStreamAndHash { + fun createInMemoryTestZip(numOfExpectedBytes: Int, content: Byte, entryName: String = "z"): InputStreamAndHash { + require(numOfExpectedBytes > 0){"Expected bytes must be greater than zero"} require(numOfExpectedBytes > 0) val baos = ByteArrayOutputStream() ZipOutputStream(baos).use { zos -> @@ -226,7 +230,7 @@ data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHa val bytes = ByteArray(arraySize) { content } val n = (numOfExpectedBytes - 1) / arraySize + 1 // same as Math.ceil(numOfExpectedBytes/arraySize). zos.setLevel(Deflater.NO_COMPRESSION) - zos.putNextEntry(ZipEntry("z")) + zos.putNextEntry(ZipEntry(entryName)) for (i in 0 until n) { zos.write(bytes, 0, arraySize) } @@ -498,3 +502,18 @@ fun SerializedBytes.checkPayloadIs(type: Class): Untrustworthy return type.castIfPossible(payloadData)?.let { UntrustworthyData(it) } ?: throw IllegalArgumentException("We were expecting a ${type.name} but we instead got a ${payloadData.javaClass.name} ($payloadData)") } + +/** + * Simple Map structure that can be used as a cache in the DJVM. + */ +fun createSimpleCache(maxSize: Int, onEject: (MutableMap.MutableEntry) -> Unit = {}): MutableMap { + return object : LinkedHashMap() { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry?): Boolean { + val eject = size > maxSize + if (eject) onEject(eldest!!) + return eject + } + } +} + +fun MutableMap.toSynchronised(): MutableMap = Collections.synchronizedMap(this) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt b/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt index 632b0c3060..82c1c35b66 100644 --- a/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt +++ b/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt @@ -14,8 +14,8 @@ interface NamedCacheFactory { * 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)) + require(!name.isBlank()){"Name must not be empty or only whitespace"} + require(allowedChars.matches(name)){"Invalid characters in cache name"} } fun buildNamed(caffeine: Caffeine, name: String): Cache diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoConstants.kt b/core/src/main/kotlin/net/corda/core/internal/NodeInfoConstants.kt similarity index 76% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoConstants.kt rename to core/src/main/kotlin/net/corda/core/internal/NodeInfoConstants.kt index 371ed2cd57..921a33fab5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoConstants.kt +++ b/core/src/main/kotlin/net/corda/core/internal/NodeInfoConstants.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal +package net.corda.core.internal // TODO: Add to Corda node.conf to allow customisation const val NODE_INFO_DIRECTORY = "additional-node-infos" diff --git a/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt b/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt index 3e774b4b6e..b8d574803e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/TransactionUtils.kt @@ -1,15 +1,23 @@ package net.corda.core.internal -import net.corda.core.contracts.ContractClassName -import net.corda.core.contracts.PrivacySalt -import net.corda.core.contracts.StateRef +import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.componentHash import net.corda.core.crypto.sha256 import net.corda.core.identity.Party +import net.corda.core.serialization.MissingAttachmentsException +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.serialize +import net.corda.core.transactions.ComponentGroup import net.corda.core.transactions.ContractUpgradeWireTransaction +import net.corda.core.transactions.FilteredComponentGroup import net.corda.core.transactions.NotaryChangeWireTransaction +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.lazyMapped import java.io.ByteArrayOutputStream +import java.security.PublicKey +import kotlin.reflect.KClass /** Constructs a [NotaryChangeWireTransaction]. */ class NotaryChangeTransactionBuilder(val inputs: List, @@ -42,4 +50,75 @@ fun combinedHash(components: Iterable): SecureHash { stream.write(it.bytes) } return stream.toByteArray().sha256() +} + +/** + * This function knows how to deserialize a transaction component group. + * + * In case the [componentGroups] is an instance of [LazyMappedList], this function will just use the original deserialized version, and avoid an unnecessary deserialization. + * The [forceDeserialize] will force deserialization. In can be used in case the SerializationContext changes. + */ +fun deserialiseComponentGroup(componentGroups: List, + clazz: KClass, + groupEnum: ComponentGroupEnum, + forceDeserialize: Boolean = false, + factory: SerializationFactory = SerializationFactory.defaultFactory, + context: SerializationContext = factory.defaultContext): List { + val group = componentGroups.firstOrNull { it.groupIndex == groupEnum.ordinal } + + if (group == null || group.components.isEmpty()) { + return emptyList() + } + + // If the componentGroup is a [LazyMappedList] it means that the original deserialized version is already available. + val components = group.components + if (!forceDeserialize && components is LazyMappedList<*, OpaqueBytes>) { + return components.originalList as List + } + + return components.lazyMapped { component, internalIndex -> + try { + factory.deserialize(component, clazz.java, context) + } catch (e: MissingAttachmentsException) { + throw e + } catch (e: Exception) { + throw Exception("Malformed transaction, $groupEnum at index $internalIndex cannot be deserialised", e) + } + } +} + +/** + * Method to deserialise Commands from its two groups: + * * COMMANDS_GROUP which contains the CommandData part + * * and SIGNERS_GROUP which contains the Signers part. + * + * This method used the [deserialiseComponentGroup] method. + */ +fun deserialiseCommands(componentGroups: List, + forceDeserialize: Boolean = false, + factory: SerializationFactory = SerializationFactory.defaultFactory, + context: SerializationContext = factory.defaultContext): List> { + // TODO: we could avoid deserialising unrelated signers. + // However, current approach ensures the transaction is not malformed + // and it will throw if any of the signers objects is not List of public keys). + val signersList: List> = uncheckedCast(deserialiseComponentGroup(componentGroups, List::class, ComponentGroupEnum.SIGNERS_GROUP, forceDeserialize)) + val commandDataList: List = deserialiseComponentGroup(componentGroups, CommandData::class, ComponentGroupEnum.COMMANDS_GROUP, forceDeserialize) + val group = componentGroups.firstOrNull { it.groupIndex == ComponentGroupEnum.COMMANDS_GROUP.ordinal } + return if (group is FilteredComponentGroup) { + check(commandDataList.size <= signersList.size) { + "Invalid Transaction. Less Signers (${signersList.size}) than CommandData (${commandDataList.size}) objects" + } + val componentHashes = group.components.mapIndexed { index, component -> componentHash(group.nonces[index], component) } + val leafIndices = componentHashes.map { group.partialMerkleTree.leafIndex(it) } + if (leafIndices.isNotEmpty()) + check(leafIndices.max()!! < signersList.size) { "Invalid Transaction. A command with no corresponding signer detected" } + commandDataList.lazyMapped { commandData, index -> Command(commandData, signersList[leafIndices[index]]) } + } else { + // It is a WireTransaction + // or a FilteredTransaction with no Commands (in which case group is null). + check(commandDataList.size == signersList.size) { + "Invalid Transaction. Sizes of CommandData (${commandDataList.size}) and Signers (${signersList.size}) do not match" + } + commandDataList.lazyMapped { commandData, index -> Command(commandData, signersList[index]) } + } } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/errors/AddressBindingException.kt b/core/src/main/kotlin/net/corda/core/internal/errors/AddressBindingException.kt index 8c597f65e4..59076ef54f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/errors/AddressBindingException.kt +++ b/core/src/main/kotlin/net/corda/core/internal/errors/AddressBindingException.kt @@ -9,7 +9,7 @@ class AddressBindingException(val addresses: Set) : CordaRun private companion object { private fun message(addresses: Set): String { - require(addresses.isNotEmpty()) + require(addresses.isNotEmpty()) { "Address list must not be empty" } return if (addresses.size > 1) { "Failed to bind on an address in ${addresses.joinToString(", ", "[", "]")}." } else { diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 3a0ee16ce0..4ef133d341 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -177,10 +177,10 @@ interface SerializationContext { fun withClassLoader(classLoader: ClassLoader): SerializationContext /** - * Helper method to return a new context based on this context with the appropriate class loader constructed from the passed attachment identifiers. - * (Requires the attachment storage to have been enabled). + * Does not do anything. */ @Throws(MissingAttachmentsException::class) + @Deprecated("There is no reason to call this. This method does not actually do anything.") fun withAttachmentsClassLoader(attachmentHashes: List): SerializationContext /** @@ -300,13 +300,8 @@ class SerializedBytes(bytes: ByteArray) : OpaqueBytes(bytes) { /** * Serializes the given object and returns a [SerializedBytes] wrapper for it. An alias for [Any.serialize] * intended to make the calling smoother for Java users. - * - * TODO: Take out the @CordaInternal annotation post-Enterprise GA when we can add API again. - * - * @suppress */ @JvmStatic - @CordaInternal @JvmOverloads fun from(obj: T, serializationFactory: SerializationFactory = SerializationFactory.defaultFactory, context: SerializationContext = serializationFactory.defaultContext): SerializedBytes { diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt new file mode 100644 index 0000000000..1a8d25437c --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -0,0 +1,161 @@ +package net.corda.core.serialization.internal + +import net.corda.core.contracts.Attachment +import net.corda.core.contracts.ContractAttachment +import net.corda.core.crypto.SecureHash +import net.corda.core.internal.VisibleForTesting +import net.corda.core.internal.isUploaderTrusted +import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializationFactory +import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.toUrl +import net.corda.core.internal.createSimpleCache +import net.corda.core.internal.toSynchronised +import java.io.IOException +import java.io.InputStream +import java.net.* + +/** + * A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only + * need to provide JAR streams, and so could be fetched from a database, local disk, etc. Constructing an + * AttachmentsClassLoader is somewhat expensive, as every attachment is scanned to ensure that there are no overlapping + * file paths. + */ +class AttachmentsClassLoader(attachments: List, parent: ClassLoader = ClassLoader.getSystemClassLoader()) : + URLClassLoader(attachments.map(::toUrl).toTypedArray(), parent) { + + companion object { + + init { + // This is required to register the AttachmentURLStreamHandlerFactory. + URL.setURLStreamHandlerFactory(AttachmentURLStreamHandlerFactory) + } + + private const val `META-INF` = "meta-inf" + private val excludeFromNoOverlapCheck = setOf( + "manifest.mf", + "license", + "license.txt", + "notice", + "notice.txt", + "index.list" + ) + + private fun shouldCheckForNoOverlap(path: String): Boolean { + if (!path.startsWith(`META-INF`)) return true + val p = path.substring(`META-INF`.length + 1) + if (p in excludeFromNoOverlapCheck) return false + if (p.endsWith(".sf") || p.endsWith(".dsa")) return false + return true + } + + @CordaSerializable + class OverlappingAttachments(val path: String) : Exception() { + override fun toString() = "Multiple attachments define a file at path $path" + } + + private fun requireNoDuplicates(attachments: List) { + val classLoaderEntries = mutableSetOf() + for (attachment in attachments) { + attachment.openAsJAR().use { jar -> + while (true) { + val entry = jar.nextJarEntry ?: break + + // We already verified that paths are not strange/game playing when we inserted the attachment + // into the storage service. So we don't need to repeat it here. + // + // We forbid files that differ only in case, or path separator to avoid issues for Windows/Mac developers where the + // filesystem tries to be case insensitive. This may break developers who attempt to use ProGuard. + // + // Also convert to Unix path separators as all resource/class lookups will expect this. + // If 2 entries have the same CRC, it means the same file is present in both attachments, so that is ok. TODO - Mike, wdyt? + val path = entry.name.toLowerCase().replace('\\', '/') + if (shouldCheckForNoOverlap(path)) { + if (path in classLoaderEntries) throw OverlappingAttachments(path) + classLoaderEntries.add(path) + } + } + } + } + } + } + + init { + require(attachments.mapNotNull { it as? ContractAttachment }.all { isUploaderTrusted(it.uploader) }) { + "Attempting to load Contract Attachments downloaded from the network" + } + + requireNoDuplicates(attachments) + } +} + +/** + * This is just a factory that provides a cache to avoid constructing expensive [AttachmentsClassLoader]s. + */ +@VisibleForTesting +internal object AttachmentsClassLoaderBuilder { + + private const val ATTACHMENT_CLASSLOADER_CACHE_SIZE = 1000 + + // This runs in the DJVM so it can't use caffeine. + private val cache: MutableMap, AttachmentsClassLoader> = createSimpleCache, AttachmentsClassLoader>(ATTACHMENT_CLASSLOADER_CACHE_SIZE) + .toSynchronised() + + fun build(attachments: List): AttachmentsClassLoader { + return cache.computeIfAbsent(attachments.map { it.id }.sorted()) { + AttachmentsClassLoader(attachments) + } + } + + fun withAttachmentsClassloaderContext(attachments: List, block: (ClassLoader) -> T): T { + + // Create classloader from the attachments. + val transactionClassLoader = AttachmentsClassLoaderBuilder.build(attachments) + + // Create a new serializationContext for the current Transaction. + val transactionSerializationContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(transactionClassLoader) + + // Deserialize all relevant classes in the transaction classloader. + return SerializationFactory.defaultFactory.withCurrentContext(transactionSerializationContext) { + block(transactionClassLoader) + } + } +} + +/** + * Registers a new internal "attachment" protocol. + * This will not be exposed as an API. + */ +object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory { + private const val attachmentScheme = "attachment" + + // TODO - what happens if this grows too large? + private val loadedAttachments = mutableMapOf().toSynchronised() + + override fun createURLStreamHandler(protocol: String): URLStreamHandler? { + return if (attachmentScheme == protocol) { + AttachmentURLStreamHandler + } else null + } + + fun toUrl(attachment: Attachment): URL { + val id = attachment.id.toString() + loadedAttachments[id] = attachment + return URL(attachmentScheme, "", -1, id, AttachmentURLStreamHandler) + } + + private object AttachmentURLStreamHandler : URLStreamHandler() { + override fun openConnection(url: URL): URLConnection { + if (url.protocol != attachmentScheme) throw IOException("Cannot handle protocol: ${url.protocol}") + val attachment = loadedAttachments[url.path] ?: throw IOException("Could not load url: $url .") + return AttachmentURLConnection(url, attachment) + } + } + + private class AttachmentURLConnection(url: URL, private val attachment: Attachment) : URLConnection(url) { + override fun getContentLengthLong(): Long = attachment.size.toLong() + override fun getInputStream(): InputStream = attachment.open() + override fun connect() { + connected = true + } + } +} diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt index 6769b73b03..f870e9aa0e 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/CheckpointSerializationAPI.kt @@ -73,13 +73,6 @@ interface CheckpointSerializationContext { */ fun withClassLoader(classLoader: ClassLoader): CheckpointSerializationContext - /** - * Helper method to return a new context based on this context with the appropriate class loader constructed from the passed attachment identifiers. - * (Requires the attachment storage to have been enabled). - */ - @Throws(MissingAttachmentsException::class) - fun withAttachmentsClassLoader(attachmentHashes: List): CheckpointSerializationContext - /** * Helper method to return a new context based on this context with the given class specifically whitelisted. */ diff --git a/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt b/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt index ddadaefc0f..0ea2c661aa 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/ContractUpgradeTransactions.kt @@ -1,5 +1,6 @@ package net.corda.core.transactions +import net.corda.core.CordaInternal import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash @@ -11,10 +12,12 @@ import net.corda.core.internal.AttachmentWithContext import net.corda.core.internal.combinedHash import net.corda.core.node.NetworkParameters import net.corda.core.node.ServicesForResolution -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.deserialize +import net.corda.core.serialization.* +import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent +import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.* +import net.corda.core.transactions.WireTransaction.Companion.resolveStateRefBinaryComponent import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58String import java.security.PublicKey @@ -35,6 +38,32 @@ data class ContractUpgradeWireTransaction( /** Required for hiding components in [ContractUpgradeFilteredTransaction]. */ val privacySalt: PrivacySalt = PrivacySalt() ) : CoreTransaction() { + + companion object { + /** + * Runs the explicit upgrade logic. + */ + @CordaInternal + internal fun calculateUpgradedState(state: TransactionState, upgradedContract: UpgradedContract, upgradedContractAttachment: Attachment): TransactionState { + // TODO: if there are encumbrance states in the inputs, just copy them across without modifying + val upgradedState: S = upgradedContract.upgrade(state.data) + val inputConstraint = state.constraint + val outputConstraint = when (inputConstraint) { + is HashAttachmentConstraint -> HashAttachmentConstraint(upgradedContractAttachment.id) + WhitelistedByZoneAttachmentConstraint -> WhitelistedByZoneAttachmentConstraint + else -> throw IllegalArgumentException("Unsupported input contract constraint $inputConstraint") + } + // TODO: re-map encumbrance pointers + return TransactionState( + data = upgradedState, + contract = upgradedContract::class.java.name, + constraint = outputConstraint, + notary = state.notary, + encumbrance = state.encumbrance + ) + } + } + override val inputs: List = serializedComponents[INPUTS.ordinal].deserialize() override val notary: Party by lazy { serializedComponents[NOTARY.ordinal].deserialize() } val legacyContractAttachmentId: SecureHash by lazy { serializedComponents[LEGACY_ATTACHMENT.ordinal].deserialize() } @@ -90,6 +119,32 @@ data class ContractUpgradeWireTransaction( ) } + private fun upgradedContract(className: ContractClassName, classLoader: ClassLoader): UpgradedContract = try { + classLoader.loadClass(className).asSubclass(UpgradedContract::class.java as Class>) + .newInstance() + } catch (e: Exception) { + throw TransactionVerificationException.ContractCreationError(id, className, e) + } + + /** + * Creates a binary serialized component for a virtual output state serialised and executed with the attachments from the transaction. + */ + @CordaInternal + internal fun resolveOutputComponent(services: ServicesForResolution, stateRef: StateRef): SerializedBytes> { + val binaryInput = resolveStateRefBinaryComponent(inputs[stateRef.index], services)!! + val legacyAttachment = services.attachments.openAttachment(legacyContractAttachmentId) + ?: throw MissingContractAttachments(emptyList()) + val upgradedAttachment = services.attachments.openAttachment(upgradedContractAttachmentId) + ?: throw MissingContractAttachments(emptyList()) + + return AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(listOf(legacyAttachment, upgradedAttachment)) { transactionClassLoader -> + val resolvedInput = binaryInput.deserialize>() + val upgradedContract = upgradedContract(upgradedContractClassName, transactionClassLoader) + val outputState = calculateUpgradedState(resolvedInput, upgradedContract, upgradedAttachment) + outputState.serialize() + } + } + /** Constructs a filtered transaction: the inputs and the notary party are always visible, while the rest are hidden. */ fun buildFilteredTransaction(): ContractUpgradeFilteredTransaction { val totalComponents = (0 until serializedComponents.size).toSet() @@ -222,22 +277,7 @@ data class ContractUpgradeLedgerTransaction( * Outputs are computed by running the contract upgrade logic on input states. This is done eagerly so that the * transaction is verified during construction. */ - override val outputs: List> = inputs.map { (state) -> - // TODO: if there are encumbrance states in the inputs, just copy them across without modifying - val upgradedState = upgradedContract.upgrade(state.data) - val inputConstraint = state.constraint - val outputConstraint = when (inputConstraint) { - is HashAttachmentConstraint -> HashAttachmentConstraint(upgradedContractAttachment.id) - WhitelistedByZoneAttachmentConstraint -> WhitelistedByZoneAttachmentConstraint - else -> throw IllegalArgumentException("Unsupported input contract constraint $inputConstraint") - } - // TODO: re-map encumbrance pointers - state.copy( - data = upgradedState, - contract = upgradedContractClassName, - constraint = outputConstraint - ) - } + override val outputs: List> = inputs.map { calculateUpgradedState(it.state, upgradedContract, upgradedContractAttachment) } /** The required signers are the set of all input states' participants. */ override val requiredSigningKeys: Set diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index c72221a204..222a04897c 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -1,22 +1,21 @@ package net.corda.core.transactions +import net.corda.core.CordaInternal import net.corda.core.KeepForDJVM import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.Party -import net.corda.core.internal.AttachmentWithContext -import net.corda.core.internal.castIfPossible -import net.corda.core.internal.checkMinimumPlatformVersion -import net.corda.core.internal.uncheckedCast +import net.corda.core.internal.* import net.corda.core.node.NetworkParameters import net.corda.core.serialization.CordaSerializable -import net.corda.core.utilities.Try +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.warnOnce import java.util.* import java.util.function.Predicate import kotlin.collections.HashSet -import net.corda.core.utilities.warnOnce /** * A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations: @@ -34,7 +33,7 @@ import net.corda.core.utilities.warnOnce // DOCSTART 1 @KeepForDJVM @CordaSerializable -data class LedgerTransaction @JvmOverloads constructor( +data class LedgerTransaction private constructor( /** The resolved input states which will be consumed/invalidated by the execution of this transaction. */ override val inputs: List>, override val outputs: List>, @@ -47,9 +46,38 @@ data class LedgerTransaction @JvmOverloads constructor( override val notary: Party?, val timeWindow: TimeWindow?, val privacySalt: PrivacySalt, - private val networkParameters: NetworkParameters? = null, - override val references: List> = emptyList() + private val networkParameters: NetworkParameters?, + override val references: List>, + val componentGroups: List?, + val resolvedInputBytes: List?, + val resolvedReferenceBytes: List? ) : FullTransaction() { + + @Deprecated("Client code should not instantiate LedgerTransaction.") + constructor( + inputs: List>, + outputs: List>, + commands: List>, + attachments: List, + id: SecureHash, + notary: Party?, + timeWindow: TimeWindow?, + privacySalt: PrivacySalt + ) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, null, emptyList(), null, null, null) + + @Deprecated("Client code should not instantiate LedgerTransaction.") + constructor( + inputs: List>, + outputs: List>, + commands: List>, + attachments: List, + id: SecureHash, + notary: Party?, + timeWindow: TimeWindow?, + privacySalt: PrivacySalt, + networkParameters: NetworkParameters? + ) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, emptyList(), null, null, null) + //DOCEND 1 init { checkBaseInvariants() @@ -58,19 +86,25 @@ data class LedgerTransaction @JvmOverloads constructor( checkEncumbrancesValid() } - private companion object { - val logger = loggerFor() - private fun contractClassFor(className: ContractClassName, classLoader: ClassLoader?): Try> { - return Try.on { - (classLoader ?: this::class.java.classLoader) - .loadClass(className) - .asSubclass(Contract::class.java) - } - } + companion object { + private val logger = loggerFor() - private fun stateToContractClass(state: TransactionState): Try> { - return contractClassFor(state.contract, state.data::class.java.classLoader) - } + @CordaInternal + internal fun makeLedgerTransaction( + inputs: List>, + outputs: List>, + commands: List>, + attachments: List, + id: SecureHash, + notary: Party?, + timeWindow: TimeWindow?, + privacySalt: PrivacySalt, + networkParameters: NetworkParameters?, + references: List>, + componentGroups: List, + resolvedInputBytes: List, + resolvedReferenceBytes: List + ) = LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, componentGroups, resolvedInputBytes, resolvedReferenceBytes) } val inputStates: List get() = inputs.map { it.state.data } @@ -88,6 +122,12 @@ data class LedgerTransaction @JvmOverloads constructor( /** * Verifies this transaction and runs contract code. At this stage it is assumed that signatures have already been verified. + + * The contract verification logic is run in a custom [AttachmentsClassLoader] created for the current transaction. + * This classloader is only used during verification and does not leak to the client code. + * + * The reason for this is that classes (contract states) deserialized in this classloader would actually be a different type from what + * the calling code would expect. * * @throws TransactionVerificationException if anything goes wrong. */ @@ -95,12 +135,17 @@ data class LedgerTransaction @JvmOverloads constructor( fun verify() { val contractAttachmentsByContract: Map = getUniqueContractAttachmentsByContract() - // TODO - verify for version downgrade - validatePackageOwnership(contractAttachmentsByContract) - validateStatesAgainstContract() - verifyConstraintsValidity(contractAttachmentsByContract) - verifyConstraints(contractAttachmentsByContract) - verifyContracts() + AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(this.attachments) { transactionClassLoader -> + + val internalTx = createInternalLedgerTransaction() + + // TODO - verify for version downgrade + validatePackageOwnership(contractAttachmentsByContract) + validateStatesAgainstContract(internalTx) + verifyConstraintsValidity(internalTx, contractAttachmentsByContract, transactionClassLoader) + verifyConstraints(internalTx, contractAttachmentsByContract) + verifyContracts(internalTx) + } } /** @@ -133,7 +178,7 @@ data class LedgerTransaction @JvmOverloads constructor( * * A warning will be written to the log if any mismatch is detected. */ - private fun validateStatesAgainstContract() = allStates.forEach(::validateStateAgainstContract) + private fun validateStatesAgainstContract(internalTx: LedgerTransaction) = internalTx.allStates.forEach { validateStateAgainstContract(it) } private fun validateStateAgainstContract(state: TransactionState) { state.data.requiredContractClassName?.let { requiredContractClassName -> @@ -150,25 +195,25 @@ data class LedgerTransaction @JvmOverloads constructor( * * Constraints should be one of the valid supported ones. * * Constraints should propagate correctly if not marked otherwise. */ - private fun verifyConstraintsValidity(contractAttachmentsByContract: Map) { + private fun verifyConstraintsValidity(internalTx: LedgerTransaction, contractAttachmentsByContract: Map, transactionClassLoader: ClassLoader) { // First check that the constraints are valid. - for (state in allStates) { + for (state in internalTx.allStates) { checkConstraintValidity(state) } // Group the inputs and outputs by contract, and for each contract verify the constraints propagation logic. // This is not required for reference states as there is nothing to propagate. - val inputContractGroups = inputs.groupBy { it.state.contract } - val outputContractGroups = outputs.groupBy { it.contract } + val inputContractGroups = internalTx.inputs.groupBy { it.state.contract } + val outputContractGroups = internalTx.outputs.groupBy { it.contract } for (contractClassName in (inputContractGroups.keys + outputContractGroups.keys)) { - if (contractClassName.contractHasAutomaticConstraintPropagation()) { + if (contractClassName.contractHasAutomaticConstraintPropagation(transactionClassLoader)) { // Verify that the constraints of output states have at least the same level of restriction as the constraints of the corresponding input states. val inputConstraints = inputContractGroups[contractClassName]?.map { it.state.constraint }?.toSet() val outputConstraints = outputContractGroups[contractClassName]?.map { it.constraint }?.toSet() outputConstraints?.forEach { outputConstraint -> inputConstraints?.forEach { inputConstraint -> - if (!(outputConstraint.canBeTransitionedFrom(inputConstraint, contractAttachmentsByContract[contractClassName]!! ))) { + if (!(outputConstraint.canBeTransitionedFrom(inputConstraint, contractAttachmentsByContract[contractClassName]!!))) { throw TransactionVerificationException.ConstraintPropagationRejection(id, contractClassName, inputConstraint, outputConstraint) } } @@ -186,8 +231,8 @@ data class LedgerTransaction @JvmOverloads constructor( * * @throws TransactionVerificationException if the constraints fail to verify */ - private fun verifyConstraints(contractAttachmentsByContract: Map) { - for (state in allStates) { + private fun verifyConstraints(internalTx: LedgerTransaction, contractAttachmentsByContract: Map) { + for (state in internalTx.allStates) { val contractAttachment = contractAttachmentsByContract[state.contract] ?: throw TransactionVerificationException.MissingAttachmentRejection(id, state.contract) @@ -226,38 +271,64 @@ data class LedgerTransaction @JvmOverloads constructor( return result } + private fun contractClassFor(className: ContractClassName, classLoader: ClassLoader): Class = try { + classLoader.loadClass(className).asSubclass(Contract::class.java) + } catch (e: Exception) { + throw TransactionVerificationException.ContractCreationError(id, className, e) + } + + private fun createInternalLedgerTransaction(): LedgerTransaction { + return if (resolvedInputBytes != null && resolvedReferenceBytes != null && componentGroups != null) { + + // Deserialize all relevant classes in the transaction classloader. + val resolvedDeserializedInputs = resolvedInputBytes.map { StateAndRef(it.serializedState.deserialize(), it.ref) } + val resolvedDeserializedReferences = resolvedReferenceBytes.map { StateAndRef(it.serializedState.deserialize(), it.ref) } + val deserializedOutputs = deserialiseComponentGroup(componentGroups, TransactionState::class, ComponentGroupEnum.OUTPUTS_GROUP, forceDeserialize = true) + val deserializedCommands = deserialiseCommands(this.componentGroups, forceDeserialize = true) + val authenticatedArgs = deserializedCommands.map { cmd -> + val parties = commands.find { it.value.javaClass.name == cmd.value.javaClass.name }!!.signingParties + CommandWithParties(cmd.signers, parties, cmd.value) + } + + val ledgerTransactionToVerify = this.copy( + inputs = resolvedDeserializedInputs, + outputs = deserializedOutputs, + commands = authenticatedArgs, + references = resolvedDeserializedReferences) + + ledgerTransactionToVerify + } else { + // This branch is only present for backwards compatibility. + // TODO - it should be removed once the constructor of LedgerTransaction is no longer public api. + logger.warn("The LedgerTransaction should not be instantiated directly from client code. Please use WireTransaction.toLedgerTransaction. The result of the verify method might not be accurate.") + this + } + } + /** * Check the transaction is contract-valid by running the verify() for each input and output state contract. * If any contract fails to verify, the whole transaction is considered to be invalid. */ - private fun verifyContracts() = inputAndOutputStates.forEach { ts -> - val contractClass = getContractClass(ts) - val contract = createContractInstance(contractClass) + private fun verifyContracts(internalTx: LedgerTransaction) { + val contractClasses = (internalTx.inputs.map { it.state } + internalTx.outputs).toSet() + .map { it.contract to contractClassFor(it.contract, it.data.javaClass.classLoader) } - try { - contract.verify(this) - } catch (e: Exception) { - throw TransactionVerificationException.ContractRejection(id, contract, e) - } - } - - // Obtain the contract class from the class name, wrapping any exception as a [ContractCreationError] - private fun getContractClass(ts: TransactionState): Class = - try { - (ts.data::class.java.classLoader ?: this::class.java.classLoader) - .loadClass(ts.contract) - .asSubclass(Contract::class.java) - } catch (e: Exception) { - throw TransactionVerificationException.ContractCreationError(id, ts.contract, e) - } - - // Obtain an instance of the contract class, wrapping any exception as a [ContractCreationError] - private fun createContractInstance(contractClass: Class): Contract = + val contractInstances = contractClasses.map { (contractClassName, contractClass) -> try { contractClass.newInstance() } catch (e: Exception) { - throw TransactionVerificationException.ContractCreationError(id, contractClass.name, e) + throw TransactionVerificationException.ContractCreationError(id, contractClassName, e) } + } + + contractInstances.forEach { contract -> + try { + contract.verify(internalTx) + } catch (e: Exception) { + throw TransactionVerificationException.ContractRejection(id, contract, e) + } + } + } /** * Make sure the notary has stayed the same. As we can't tell how inputs and outputs connect, if there @@ -286,7 +357,8 @@ data class LedgerTransaction @JvmOverloads constructor( // b) the number of outputs can contain the encumbrance // c) the bi-directionality (full cycle) property is satisfied // d) encumbered output states are assigned to the same notary. - val statesAndEncumbrance = outputs.withIndex().filter { it.value.encumbrance != null }.map { Pair(it.index, it.value.encumbrance!!) } + val statesAndEncumbrance = outputs.withIndex().filter { it.value.encumbrance != null } + .map { Pair(it.index, it.value.encumbrance!!) } if (!statesAndEncumbrance.isEmpty()) { checkBidirectionalOutputEncumbrances(statesAndEncumbrance) checkNotariesOutputEncumbrance(statesAndEncumbrance) diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt index 811b3dfab6..e91f867067 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt @@ -6,14 +6,14 @@ import net.corda.core.contracts.* import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.crypto.* import net.corda.core.identity.Party -import net.corda.core.internal.LazyMappedList -import net.corda.core.internal.uncheckedCast -import net.corda.core.serialization.* +import net.corda.core.internal.deserialiseCommands +import net.corda.core.internal.deserialiseComponentGroup +import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializedBytes +import net.corda.core.serialization.deserialize import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.lazyMapped import java.security.PublicKey import java.util.function.Predicate -import kotlin.reflect.KClass /** * Implemented by [WireTransaction] and [FilteredTransaction]. A TraversableTransaction allows you to iterate @@ -23,27 +23,27 @@ import kotlin.reflect.KClass */ abstract class TraversableTransaction(open val componentGroups: List) : CoreTransaction() { /** Hashes of the ZIP/JAR files that are needed to interpret the contents of this wire transaction. */ - val attachments: List = deserialiseComponentGroup(SecureHash::class, ATTACHMENTS_GROUP) + val attachments: List = deserialiseComponentGroup(componentGroups, SecureHash::class, ATTACHMENTS_GROUP) /** Pointers to the input states on the ledger, identified by (tx identity hash, output index). */ - override val inputs: List = deserialiseComponentGroup(StateRef::class, INPUTS_GROUP) + override val inputs: List = deserialiseComponentGroup(componentGroups, StateRef::class, INPUTS_GROUP) /** Pointers to reference states, identified by (tx identity hash, output index). */ - override val references: List = deserialiseComponentGroup(StateRef::class, REFERENCES_GROUP) + override val references: List = deserialiseComponentGroup(componentGroups, StateRef::class, REFERENCES_GROUP) - override val outputs: List> = deserialiseComponentGroup(TransactionState::class, OUTPUTS_GROUP, attachmentsContext = true) + override val outputs: List> = deserialiseComponentGroup(componentGroups, TransactionState::class, OUTPUTS_GROUP) /** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */ - val commands: List> = deserialiseCommands() + val commands: List> = deserialiseCommands(componentGroups) override val notary: Party? = let { - val notaries: List = deserialiseComponentGroup(Party::class, NOTARY_GROUP) + val notaries: List = deserialiseComponentGroup(componentGroups, Party::class, NOTARY_GROUP) check(notaries.size <= 1) { "Invalid Transaction. More than 1 notary party detected." } notaries.firstOrNull() } val timeWindow: TimeWindow? = let { - val timeWindows: List = deserialiseComponentGroup(TimeWindow::class, TIMEWINDOW_GROUP) + val timeWindows: List = deserialiseComponentGroup(componentGroups, TimeWindow::class, TIMEWINDOW_GROUP) check(timeWindows.size <= 1) { "Invalid Transaction. More than 1 time-window detected." } timeWindows.firstOrNull() } @@ -66,65 +66,6 @@ abstract class TraversableTransaction(open val componentGroups: List deserialiseComponentGroup(clazz: KClass, - groupEnum: ComponentGroupEnum, - attachmentsContext: Boolean = false): List { - val group = componentGroups.firstOrNull { it.groupIndex == groupEnum.ordinal } - - if (group == null || group.components.isEmpty()) { - return emptyList() - } - - // If the componentGroup is a [LazyMappedList] it means that the original deserialized version is already available. - val components = group.components - if (components is LazyMappedList<*, OpaqueBytes>) { - return components.originalList as List - } - - val factory = SerializationFactory.defaultFactory - val context = factory.defaultContext.let { if (attachmentsContext) it.withAttachmentsClassLoader(attachments) else it } - - return components.lazyMapped { component, internalIndex -> - try { - factory.deserialize(component, clazz.java , context) - } catch (e: MissingAttachmentsException) { - throw e - } catch (e: Exception) { - throw Exception("Malformed transaction, $groupEnum at index $internalIndex cannot be deserialised", e) - } - } - } - - // Method to deserialise Commands from its two groups: - // COMMANDS_GROUP which contains the CommandData part - // and SIGNERS_GROUP which contains the Signers part. - private fun deserialiseCommands(): List> { - // TODO: we could avoid deserialising unrelated signers. - // However, current approach ensures the transaction is not malformed - // and it will throw if any of the signers objects is not List of public keys). - val signersList: List> = uncheckedCast(deserialiseComponentGroup(List::class, SIGNERS_GROUP)) - val commandDataList: List = deserialiseComponentGroup(CommandData::class, COMMANDS_GROUP, attachmentsContext = true) - val group = componentGroups.firstOrNull { it.groupIndex == COMMANDS_GROUP.ordinal } - return if (group is FilteredComponentGroup) { - check(commandDataList.size <= signersList.size) { - "Invalid Transaction. Less Signers (${signersList.size}) than CommandData (${commandDataList.size}) objects" - } - val componentHashes = group.components.mapIndexed { index, component -> componentHash(group.nonces[index], component) } - val leafIndices = componentHashes.map { group.partialMerkleTree.leafIndex(it) } - if (leafIndices.isNotEmpty()) - check(leafIndices.max()!! < signersList.size) { "Invalid Transaction. A command with no corresponding signer detected" } - commandDataList.lazyMapped { commandData, index -> Command(commandData, signersList[leafIndices[index]]) } - } else { - // It is a WireTransaction - // or a FilteredTransaction with no Commands (in which case group is null). - check(commandDataList.size == signersList.size) { - "Invalid Transaction. Sizes of CommandData (${commandDataList.size}) and Signers (${signersList.size}) do not match" - } - commandDataList.lazyMapped { commandData, index -> Command(commandData, signersList[index]) } - } - } } /** diff --git a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt index 55f329540d..98c0ae4678 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt @@ -1,5 +1,6 @@ package net.corda.core.transactions +import net.corda.core.CordaInternal import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.contracts.* @@ -10,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.transactions.NotaryChangeWireTransaction.Component.* @@ -75,6 +77,20 @@ data class NotaryChangeWireTransaction( @DeleteForDJVM fun resolve(services: ServiceHub, sigs: List) = resolve(services as ServicesForResolution, sigs) + /** + * This should return a serialized virtual output state, that will be used to verify spending transactions. + * The binary output should not depend on the classpath of the node that is verifying the transaction. + * + * Ideally the serialization engine would support partial deserialization so that only the Notary ( and the encumbrance can be replaced from the binary input state) + * + * + * TODO - currently this uses the main classloader. + */ + @CordaInternal + internal fun resolveOutputComponent(services: ServicesForResolution, stateRef: StateRef): SerializedBytes> { + return services.loadState(stateRef).serialize() + } + enum class Component { INPUTS, NOTARY, NEW_NOTARY } diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 6db9f4c673..19cab5b63a 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -307,7 +307,7 @@ open class TransactionBuilder @JvmOverloads constructor( } // The final step is to resolve AutomaticPlaceholderConstraint. - val automaticConstraintPropagation = contractClassName.contractHasAutomaticConstraintPropagation(serializationContext?.deserializationClassLoader) + val automaticConstraintPropagation = contractClassName.contractHasAutomaticConstraintPropagation(inputsAndOutputs.first().data::class.java.classLoader) // When automaticConstraintPropagation is disabled for a contract, output states must an explicit Constraint. require(automaticConstraintPropagation) { "Contract $contractClassName was marked with @NoConstraintPropagation, which means the constraint of the output states has to be set explicitly." } diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index 6ff8983745..a7ecc86dce 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -7,11 +7,15 @@ import net.corda.core.contracts.* import net.corda.core.contracts.ComponentGroupEnum.* import net.corda.core.crypto.* import net.corda.core.identity.Party +import net.corda.core.internal.SerializedStateAndRef import net.corda.core.internal.Emoji import net.corda.core.node.NetworkParameters +import net.corda.core.node.ServiceHub import net.corda.core.node.ServicesForResolution import net.corda.core.node.services.AttachmentId import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializedBytes +import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.lazyMapped @@ -99,7 +103,7 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr return toLedgerTransactionInternal( resolveIdentity = { services.identityService.partyFromKey(it) }, resolveAttachment = { services.attachments.openAttachment(it) }, - resolveStateRef = { services.loadState(it) }, + resolveStateRefComponent = { resolveStateRefBinaryComponent(it, services) }, networkParameters = services.networkParameters ) } @@ -119,13 +123,14 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr resolveStateRef: (StateRef) -> TransactionState<*>?, @Suppress("UNUSED_PARAMETER") resolveContractAttachment: (TransactionState) -> AttachmentId? ): LedgerTransaction { - return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef, null) + // This reverts to serializing the resolved transaction state. + return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, { stateRef -> resolveStateRef(stateRef)?.serialize() }, null) } private fun toLedgerTransactionInternal( resolveIdentity: (PublicKey) -> Party?, resolveAttachment: (SecureHash) -> Attachment?, - resolveStateRef: (StateRef) -> TransactionState<*>?, + resolveStateRefComponent: (StateRef) -> SerializedBytes>?, networkParameters: NetworkParameters? ): LedgerTransaction { // Look up public keys to authenticated identities. @@ -133,20 +138,38 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr val parties = cmd.signers.mapNotNull { pk -> resolveIdentity(pk) } CommandWithParties(cmd.signers, parties, cmd.value) } - val resolvedInputs = inputs.lazyMapped { ref, _ -> - resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash) + + val resolvedInputBytes = inputs.map { ref -> + SerializedStateAndRef(resolveStateRefComponent(ref) + ?: throw TransactionResolutionException(ref.txhash), ref) } - val resolvedReferences = references.lazyMapped { ref, _ -> - resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash) + val resolvedInputs = resolvedInputBytes.lazyMapped { (serialized, ref), _ -> + StateAndRef(serialized.deserialize(), ref) } + + val resolvedReferenceBytes = references.map { ref -> + SerializedStateAndRef(resolveStateRefComponent(ref) + ?: throw TransactionResolutionException(ref.txhash), ref) + } + val resolvedReferences = resolvedReferenceBytes.lazyMapped { (serialized, ref), _ -> + StateAndRef(serialized.deserialize(), ref) + } + val attachments = attachments.lazyMapped { att, _ -> resolveAttachment(att) ?: throw AttachmentResolutionException(att) } - val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters, resolvedReferences) - checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: 10485760) + + val ltx = LedgerTransaction.makeLedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters, resolvedReferences, componentGroups, resolvedInputBytes, resolvedReferenceBytes) + + checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: DEFAULT_MAX_TX_SIZE) + return ltx } + /** + * Deterministic function that checks if the transaction is below the maximum allowed size. + * It uses the binary representation of transactions. + */ private fun checkTransactionSize(ltx: LedgerTransaction, maxTransactionSize: Int) { var remainingTransactionSize = maxTransactionSize @@ -164,9 +187,8 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr // it's likely that the same underlying Attachment CorDapp will occur more than once so we dedup on the attachment id. ltx.attachments.distinctBy { it.id }.forEach { minus(it.size) } - // TODO - these can be optimized by creating a LazyStateAndRef class, that just stores (a pointer) the serialized output componentGroup from the previous transaction. - minus(ltx.references.serialize().size) - minus(ltx.inputs.serialize().size) + minus(ltx.resolvedInputBytes!!.sumBy { it.serializedState.size }) + minus(ltx.resolvedReferenceBytes!!.sumBy { it.serializedState.size }) // For Commands and outputs we can use the component groups as they are already serialized. minus(componentGroupSize(COMMANDS_GROUP)) @@ -253,6 +275,8 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr } companion object { + private const val DEFAULT_MAX_TX_SIZE = 10485760 + /** * Creating list of [ComponentGroup] used in one of the constructors of [WireTransaction] required * for backwards compatibility purposes. @@ -281,6 +305,28 @@ class WireTransaction(componentGroups: List, val privacySalt: Pr if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(SIGNERS_GROUP.ordinal, commands.map { it.signers }.lazyMapped(serialize))) return componentGroupMap } + + /** + * This is the main logic that knows how to retrieve the binary representation of [StateRef]s. + * + * For [ContractUpgradeWireTransaction] or [NotaryChangeWireTransaction] it knows how to recreate the output state in the correct classloader independent of the node's classpath. + */ + @CordaInternal + fun resolveStateRefBinaryComponent(stateRef: StateRef, services: ServicesForResolution): SerializedBytes>? { + return if (services is ServiceHub) { + val coreTransaction = services.validatedTransactions.getTransaction(stateRef.txhash)?.coreTransaction + ?: throw TransactionResolutionException(stateRef.txhash) + when (coreTransaction) { + is WireTransaction -> coreTransaction.componentGroups.firstOrNull { it.groupIndex == ComponentGroupEnum.OUTPUTS_GROUP.ordinal }?.components?.get(stateRef.index) as SerializedBytes>? + is ContractUpgradeWireTransaction -> coreTransaction.resolveOutputComponent(services, stateRef) + is NotaryChangeWireTransaction -> coreTransaction.resolveOutputComponent(services, stateRef) + else -> throw UnsupportedOperationException("Attempting to resolve input ${stateRef.index} of a ${coreTransaction.javaClass} transaction. This is not supported.") + } + } else { + // For backwards compatibility revert to using the node classloader. + services.loadState(stateRef).serialize() + } + } } @DeleteForDJVM diff --git a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt index 7190e0c770..296929d29b 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt @@ -68,8 +68,8 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si * This method cannot be used to get bytes before [offset] or after [offset]+[size], and never makes a new array. */ fun slice(start: Int = 0, end: Int = size): ByteBuffer { - require(start >= 0) - require(end >= 0) + require(start >= 0) { "Starting index must be greater than or equal to 0" } + require(end >= 0){"End index must be greater or equal to 0"} val clampedStart = min(start, size) val clampedEnd = min(end, size) return ByteBuffer.wrap(_bytes, offset + clampedStart, max(0, clampedEnd - clampedStart)).asReadOnlyBuffer() @@ -155,7 +155,7 @@ open class OpaqueBytes(bytes: ByteArray) : ByteSequence(bytes, 0, bytes.size) { } init { - require(bytes.isNotEmpty()) + require(bytes.isNotEmpty()) { "Byte Array must not be empty" } } /** @@ -193,7 +193,7 @@ fun String.parseAsHex(): ByteArray = DatatypeConverter.parseHexBinary(this) @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 && offset + size <= bytes.size) + require(offset >= 0 && offset < bytes.size) { "Offset must be greater than or equal to 0, and less than the size of the backing array" } + require(size >= 0 && offset + size <= bytes.size) { "Sub-sequence size must be greater than or equal to 0, and less than the size of the backing array" } } } diff --git a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt index 8ef7904a75..56a30be9a5 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt @@ -6,6 +6,7 @@ import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.internal.LazyMappedList import net.corda.core.internal.concurrent.get +import net.corda.core.internal.createSimpleCache import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializable import org.slf4j.Logger @@ -149,9 +150,7 @@ fun Future.getOrThrow(timeout: Duration? = null): V = try { fun List.lazyMapped(transform: (T, Int) -> U): List = LazyMappedList(this, transform) private const val MAX_SIZE = 100 -private val warnings = Collections.newSetFromMap(object : LinkedHashMap() { - override fun removeEldestEntry(eldest: MutableMap.MutableEntry?) = size > MAX_SIZE -}) +private val warnings = Collections.newSetFromMap(createSimpleCache(MAX_SIZE)) /** * Utility to help log a warning message only once. @@ -163,4 +162,4 @@ fun Logger.warnOnce(warning: String) { warnings.add(warning) this.warn(warning) } -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index e2a2e22b71..b7c5b8a4cd 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -14,18 +14,13 @@ import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.hash import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.BOB_NAME -import net.corda.testing.core.makeUnique -import net.corda.testing.core.singleIdentity +import net.corda.testing.core.* +import net.corda.testing.internal.fakeAttachment import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.TestStartedNode import org.junit.AfterClass import org.junit.Test -import java.io.ByteArrayOutputStream -import java.util.jar.JarOutputStream -import java.util.zip.ZipEntry class AttachmentTests : WithMockNet { companion object { @@ -46,7 +41,7 @@ class AttachmentTests : WithMockNet { @Test fun `download and store`() { // Insert an attachment into node zero's store directly. - val id = aliceNode.importAttachment(fakeAttachment()) + val id = aliceNode.importAttachment(fakeAttachment("file1.txt", "Some useful content")) // Get node one to run a flow to fetch it and insert it. assert.that( @@ -87,7 +82,7 @@ class AttachmentTests : WithMockNet { val badAlice = badAliceNode.info.singleIdentity() // Insert an attachment into node zero's store directly. - val attachment = fakeAttachment() + val attachment = fakeAttachment("file1.txt", "Some useful content") val id = badAliceNode.importAttachment(attachment) // Corrupt its store. @@ -134,18 +129,6 @@ class AttachmentTests : WithMockNet { } }).apply { registerInitiatedFlow(FetchAttachmentsResponse::class.java) } - private fun fakeAttachment(): ByteArray = - ByteArrayOutputStream().use { baos -> - JarOutputStream(baos).use { jos -> - jos.putNextEntry(ZipEntry("file1.txt")) - jos.writer().apply { - append("Some useful content") - flush() - } - jos.closeEntry() - } - baos.toByteArray() - } //endregion //region Operations diff --git a/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderSerializationTests.kt b/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderSerializationTests.kt new file mode 100644 index 0000000000..c3364eeba3 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderSerializationTests.kt @@ -0,0 +1,98 @@ +package net.corda.core.transactions + +import net.corda.core.contracts.Contract +import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.declaredField +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder +import net.corda.core.serialization.serialize +import net.corda.core.utilities.ByteSequence +import net.corda.core.utilities.OpaqueBytes +import net.corda.nodeapi.DummyContractBackdoor +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.internal.fakeAttachment +import net.corda.testing.services.MockAttachmentStorage +import org.apache.commons.io.IOUtils +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import java.io.NotSerializableException +import java.net.URL +import kotlin.test.assertFailsWith + +class AttachmentsClassLoaderSerializationTests { + + companion object { + val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderSerializationTests::class.java.getResource("isolated.jar") + private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" + } + + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule() + + val storage = MockAttachmentStorage() + + @Test + fun `Can serialize and deserialize with an attachment classloader`() { + + val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party + val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party + + val isolatedId = storage.importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar") + val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar") + val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar") + + val serialisedState = AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(arrayOf(isolatedId, att1, att2).map { storage.openAttachment(it)!! }) { classLoader -> + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader) + val contract = contractClass.newInstance() as Contract + assertEquals("helloworld", contract.declaredField("magicString").value) + + val txt = IOUtils.toString(classLoader.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) + assertEquals("some data", txt) + + val state = (contract as DummyContractBackdoor).generateInitial(MEGA_CORP.ref(1), 1, DUMMY_NOTARY).outputStates().first() + val serialisedState = state.serialize() + + val state1 = serialisedState.deserialize() + assertEquals(state, state1) + serialisedState + } + + assertFailsWith { + serialisedState.deserialize() + } + } + + // These tests are not Attachment specific. Should they be removed? + @Test + fun `test serialization of SecureHash`() { + val secureHash = SecureHash.randomSHA256() + val bytes = secureHash.serialize() + val copiedSecuredHash = bytes.deserialize() + + assertEquals(secureHash, copiedSecuredHash) + } + + @Test + fun `test serialization of OpaqueBytes`() { + val opaqueBytes = OpaqueBytes("0123456789".toByteArray()) + val bytes = opaqueBytes.serialize() + val copiedOpaqueBytes = bytes.deserialize() + + assertEquals(opaqueBytes, copiedOpaqueBytes) + } + + @Test + fun `test serialization of sub-sequence OpaqueBytes`() { + val bytesSequence = ByteSequence.of("0123456789".toByteArray(), 3, 2) + val bytes = bytesSequence.serialize() + val copiedBytesSequence = bytes.deserialize() + + assertEquals(bytesSequence, copiedBytesSequence) + } +} + diff --git a/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderTests.kt b/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderTests.kt new file mode 100644 index 0000000000..0fe3727df8 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/transactions/AttachmentsClassLoaderTests.kt @@ -0,0 +1,102 @@ +package net.corda.core.transactions + +import net.corda.core.contracts.Attachment +import net.corda.core.contracts.Contract +import net.corda.core.internal.declaredField +import net.corda.core.serialization.internal.AttachmentsClassLoader +import net.corda.testing.internal.fakeAttachment +import net.corda.testing.services.MockAttachmentStorage +import org.apache.commons.io.IOUtils +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Test +import java.io.ByteArrayOutputStream +import java.net.URL +import kotlin.test.assertFailsWith + +class AttachmentsClassLoaderTests { + + companion object { + val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated.jar") + private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" + + private fun readAttachment(attachment: Attachment, filepath: String): ByteArray { + ByteArrayOutputStream().use { + attachment.extractFile(filepath, it) + return it.toByteArray() + } + } + } + + val storage = MockAttachmentStorage() + + @Test + fun `Loading AnotherDummyContract without using the AttachmentsClassLoader fails`() { + assertFailsWith { + Class.forName(ISOLATED_CONTRACT_CLASS_NAME) + } + } + + @Test + fun `Dynamically load AnotherDummyContract from isolated contracts jar using the AttachmentsClassLoader`() { + val isolatedId = storage.importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar") + + val classloader = AttachmentsClassLoader(listOf(storage.openAttachment(isolatedId)!!)) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader) + val contract = contractClass.newInstance() as Contract + assertEquals("helloworld", contract.declaredField("magicString").value) + } + + @Test + fun `Load text resources from AttachmentsClassLoader`() { + val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar") + val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar") + + val cl = AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! }) + val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) + assertEquals("some data", txt) + + val txt1 = IOUtils.toString(cl.getResourceAsStream("file2.txt"), Charsets.UTF_8.name()) + assertEquals("some other data", txt1) + } + + @Test + fun `Test overlapping file exception`() { + val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar") + val att2 = storage.importAttachment(fakeAttachment("file1.txt", "some other data").inputStream(), "app", "file2.jar") + + assertFailsWith(AttachmentsClassLoader.Companion.OverlappingAttachments::class) { + AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! }) + } + } + + @Test + fun `No overlapping exception thrown on certain META-INF files`() { + listOf("meta-inf/manifest.mf", "meta-inf/license", "meta-inf/test.dsa", "meta-inf/test.sf").forEach { path -> + val att1 = storage.importAttachment(fakeAttachment(path, "some data").inputStream(), "app", "file1.jar") + val att2 = storage.importAttachment(fakeAttachment(path, "some other data").inputStream(), "app", "file2.jar") + + AttachmentsClassLoader(arrayOf(att1, att2).map { storage.openAttachment(it)!! }) + } + } + + @Test + fun `Check platform independent path handling in attachment jars`() { + val storage = MockAttachmentStorage() + + val att1 = storage.importAttachment(fakeAttachment("/folder1/foldera/file1.txt", "some data").inputStream(), "app", "file1.jar") + val att2 = storage.importAttachment(fakeAttachment("\\folder1\\folderb\\file2.txt", "some other data").inputStream(), "app", "file2.jar") + + val data1a = readAttachment(storage.openAttachment(att1)!!, "/folder1/foldera/file1.txt") + assertArrayEquals("some data".toByteArray(), data1a) + + val data1b = readAttachment(storage.openAttachment(att1)!!, "\\folder1\\foldera\\file1.txt") + assertArrayEquals("some data".toByteArray(), data1b) + + val data2a = readAttachment(storage.openAttachment(att2)!!, "\\folder1\\folderb\\file2.txt") + assertArrayEquals("some other data".toByteArray(), data2a) + + val data2b = readAttachment(storage.openAttachment(att2)!!, "/folder1/folderb/file2.txt") + assertArrayEquals("some other data".toByteArray(), data2b) + } +} diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt index 32f9105a1c..b37a34fc97 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionTests.kt @@ -10,6 +10,7 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.core.* import net.corda.testing.internal.createWireTransaction +import net.corda.testing.internal.fakeAttachment import net.corda.testing.internal.rigorousMock import org.junit.Rule import org.junit.Test @@ -118,7 +119,8 @@ class TransactionTests { val commands = emptyList>() val attachments = listOf(ContractAttachment(rigorousMock().also { doReturn(SecureHash.zeroHash).whenever(it).id - }, DummyContract.PROGRAM_ID)) + doReturn(fakeAttachment("nothing", "nada").inputStream()).whenever(it).open() + }, DummyContract.PROGRAM_ID, uploader = "app")) val id = SecureHash.randomSHA256() val timeWindow: TimeWindow? = null val privacySalt = PrivacySalt() diff --git a/serialization/src/test/kotlin/net/corda/nodeapi/DummyContractBackdoor.kt b/core/src/test/kotlin/net/corda/nodeapi/DummyContractBackdoor.kt similarity index 100% rename from serialization/src/test/kotlin/net/corda/nodeapi/DummyContractBackdoor.kt rename to core/src/test/kotlin/net/corda/nodeapi/DummyContractBackdoor.kt diff --git a/core/src/test/resources/net/corda/core/transactions/isolated.jar b/core/src/test/resources/net/corda/core/transactions/isolated.jar new file mode 100644 index 0000000000..17bf0c2436 Binary files /dev/null and b/core/src/test/resources/net/corda/core/transactions/isolated.jar differ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 5080f61a1a..912da63c6f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,11 @@ release, see :doc:`upgrade-notes`. Unreleased ---------- +* Deprecated `SerializationContext.withAttachmentsClassLoader`. This functionality has always been disabled by flags +and there is no reason for a CorDapp developer to use it. It is just an internal implementation detail of Corda. + +* Deprecated the `LedgerTransaction` constructor. No client code should call it directly. LedgerTransactions can be created from WireTransactions if required. + * Introduced new optional network bootstrapper command line options (--register-package-owner, --unregister-package-owner) to register/unregister a java package namespace with an associated owner in the network parameter packageOwnership whitelist. diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 898e4c4c52..8cf7d1e452 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -48,8 +48,10 @@ Here are the contents of the ``reference.conf`` file for Corda Enterprise: Fields ------ -The available config fields are listed below. ``baseDirectory`` is available as a substitution value and contains the -absolute path to the node's base directory. + +.. note:: All fields can be used with placeholders for environment variables. For example: ``${NODE_TRUST_STORE_PASSWORD}`` would be replaced by the contents of environment variable ``NODE_TRUST_STORE_PASSWORD``. See: `Hiding Sensitive Data`_ + +The available config fields are listed below. :myLegalName: The legal identity of the node. This acts as a human-readable alias to the node's public key and can be used with the network map to look up the node's info. This is the name that is used in the node's certificates (either when requesting them @@ -395,3 +397,47 @@ Together with the above configuration `tlsCertCrlIssuer` option needs to be set This set-up ensures that the TLS-level certificates are embedded with the CRL distribution point referencing the CRL issued by R3. In cases where a proprietary CRL infrastructure is provided those values need to be changed accordingly. + +Hiding Sensitive Data +--------------------- +A frequent requirement is that configuration files must not expose passwords to unauthorised readers. By leveraging environment variables, it is possible to hide passwords and other similar fields. + +Take a simple node config that wishes to protect the node cryptographic stores: + +.. parsed-literal:: + + myLegalName : "O=PasswordProtectedNode,OU=corda,L=London,C=GB" + keyStorePassword : ${KEY_PASS} + trustStorePassword : ${TRUST_PASS} + p2pAddress : "localhost:12345" + devMode : false + compatibilityZoneURL : "https://cz.corda.net" + +By delegating to a password store, and using `command substitution` it is possible to ensure that sensitive passwords never appear in plain text. +The below examples are of loading Corda with the KEY_PASS and TRUST_PASS variables read from a program named ``corporatePasswordStore``. + + +Bash +~~~~ + +.. sourcecode:: shell + + KEY_PASS=$(corporatePasswordStore --cordaKeyStorePassword) TRUST_PASS=$(corporatePasswordStore --cordaTrustStorePassword) java -jar corda.jar + +Windows PowerShell +~~~~~~~~~~~~~~~~~~ + +.. sourcecode:: shell + + $env:KEY_PASS=$(corporatePasswordStore --cordaKeyStorePassword); $env:TRUST_PASS=$(corporatePasswordStore --cordaTrustStorePassword); java -jar corda.jar + + +For launching on Windows without PowerShell, it is not possible to perform command substitution, and so the variables must be specified manually, for example: + +.. sourcecode:: shell + + SET KEY_PASS=mypassword & SET TRUST_PASS=mypassword & java -jar corda.jar + +.. warning:: If this approach is taken, the passwords will appear in the windows command prompt history. + + diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 6f5e289fff..3995b4bb13 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -46,9 +46,13 @@ Several ``ext`` variables are used in a CorDapp's ``build.gradle`` file to defin ``corda_gradle_plugins_versions`` are given in the form ``major.minor.patch``. You should use the same ``major`` and ``minor`` versions as the Corda version you are using, and the latest ``patch`` version. A list of all the available -versions can be found here: https://bintray.com/r3/corda/cordapp. If in doubt, you should base yourself on the version numbers used in the ``build.gradle`` file of the `Kotlin CorDapp Template `_ and the `Java CorDapp Template `_. +versions can be found here: https://bintray.com/r3/corda/cordapp. If in doubt, you should base yourself on the version +numbers used in the ``build.gradle`` file of the +`Kotlin CorDapp Template `_ and the +`Java CorDapp Template `_. -For example, to use version 3.0 of Corda, version 3.0.8 of the Corda gradle plugins, version 0.7.9 of Quasar, and version 1.1.60 of Kotlin, you'd write: +For example, to use version 3.0 of Corda, version 3.0.8 of the Corda gradle plugins, version 0.7.9 of Quasar, and +version 1.1.60 of Kotlin, you'd write: .. sourcecode:: groovy @@ -70,19 +74,55 @@ The ``cordformation`` plugin adds two new gradle configurations: configurations should be used for any Corda dependency (e.g. ``corda-core``, ``corda-node``) in order to prevent a dependency from being included twice (once in the CorDapp JAR and once in the Corda JARs). -To build against Corda, you must add the following to your ``build.gradle`` file: +Here are some guidelines for Corda dependencies: -* ``net.corda:corda:$corda_release_version`` as a ``cordaRuntime`` dependency -* Each Corda compile dependency (eg ``net.corda:corda-core:$corda_release_version``) as a ``cordaCompile`` dependency +* When building a CorDapp, you should always include ``net.corda:corda-core:$corda_release_version`` as a + ``cordaCompile`` dependency, and ``net.corda:corda:$corda_release_version`` as a ``cordaRuntime`` dependency -You may also want to add: +* When building an RPC client that communicates with a node (e.g. a webserver), you should include + ``net.corda:corda-rpc:$corda_release_version`` as a ``cordaCompile`` dependency -* ``net.corda:corda-test-utils:$corda_release_version`` as a ``testCompile`` dependency, in order to use Corda's test +* When you need to use the network bootstrapper to bootstrap a local network (e.g. when using ``Cordformation``), you + should include ``net.corda:corda-node-api:$corda_release_version`` as a ``cordaCompile`` dependency + +* To use Corda's test frameworks, add ``net.corda:corda-test-utils:$corda_release_version`` as a ``testCompile`` + dependency. Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency + +* Any other Corda dependencies you need should be included as ``cordaCompile`` dependencies + +Here is an overview of the various Corda dependencies: + +* ``corda`` - The Corda fat JAR. Do not use as a compile dependency. Required as a ``cordaRuntime`` dependency when + using ``Cordformation`` +* ``corda-confidential-identities`` - A part of the core Corda libraries. Automatically pulled in by other libraries +* ``corda-core`` - Usually automatically included by another dependency, contains core Corda utilities, model, and + functionality. Include manually if the utilities are useful or you are writing a library for Corda +* ``corda-core-deterministic`` - Used by the Corda node for deterministic contracts. Not likely to be used externally +* ``corda-djvm`` - Used by the Corda node for deterministic contracts. Not likely to be used externally +* ``corda-finance`` - The Corda finance CorDapp. Only include as a ``cordaCompile`` dependency if using as a dependent + Cordapp or if you need access to the Corda finance types. Use as a ``cordapp`` dependency if using as a CorDapp + dependency (see below) +* ``corda-jackson`` - Corda Jackson support. Use if you plan to serialise Corda objects to and/or from JSON +* ``corda-jfx`` - JavaFX utilities with some Corda-specific models and utilities. Only use with JavaFX apps +* ``corda-mock`` - A small library of useful mocks. Use if the classes are useful to you +* ``corda-node`` - The Corda node. Do not depend on. Used only by the Corda fat JAR and indirectly in testing frameworks -* ``net.corda:corda-webserver:$corda_release_version`` as a ``cordaRuntime`` dependency, in order to use Corda's - built-in development webserver - -.. warning:: Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency. +* ``corda-node-api`` - The node API. Required to bootstrap a local network +* ``corda-node-driver`` - Testing utility for programmatically starting nodes from JVM languages. Use for tests +* ``corda-notary-bft-smart`` - A Corda notary implementation +* ``corda-notary-raft`` - A Corda notary implementation +* ``corda-rpc`` - The Corda RPC client library. Used when writing an RPC client +* ``corda-serialization`` - The Corda core serialization library. Automatically included by other dependencies +* ``corda-serialization-deterministic`` - The Corda core serialization library. Automatically included by other + dependencies +* ``corda-shell`` - Used by the Corda node. Never depend on directly +* ``corda-test-common`` - A common test library. Automatically included by other test libraries +* ``corda-test-utils`` - Used when writing tests against Corda/Cordapps +* ``corda-tools-explorer`` - The Node Explorer tool. Do not depend on +* ``corda-tools-network-bootstrapper`` - The Network Builder tool. Useful in build scripts +* ``corda-tools-shell-cli`` - The Shell CLI tool. Useful in build scripts +* ``corda-webserver-impl`` - The Corda webserver fat JAR. Deprecated. Usually only used by build scripts +* ``corda-websever`` - The Corda webserver library. Deprecated. Use a standard webserver library such as Spring instead Dependencies on other CorDapps ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,19 +197,19 @@ The example ``cordapp`` plugin with plugin ``signing`` configuration: } //... -CorDapp auto-signing allows to use signature constraints for contracts from the CorDapp -without need to create a keystore and configure the ``cordapp`` plugin. -For production deployment ensure to sign the CorDapp using your own certificate e.g. by setting system properties to point to an external keystore -or by disabling signing in ``cordapp`` plugin and signing the CordDapp JAR downstream in your build pipeline. -CorDapp signed by Corda development certificate is accepted by Corda node only when running in the development mode. -In case CordDapp signed by the (default) development key is run on node in the production mode (e.g. for testing), -the node may be set to accept the development key by adding the ``cordappSignerKeyFingerprintBlacklist = []`` property set to empty list -(see :ref:`Configuring a node `). +CorDapp auto-signing allows to use signature constraints for contracts from the CorDapp without need to create a +keystore and configure the ``cordapp`` plugin. For production deployment ensure to sign the CorDapp using your own +certificate e.g. by setting system properties to point to an external keystore or by disabling signing in ``cordapp`` +plugin and signing the CordDapp JAR downstream in your build pipeline. CorDapp signed by Corda development certificate +is accepted by Corda node only when running in the development mode. In case CordDapp signed by the (default) +development key is run on node in the production mode (e.g. for testing), the node may be set to accept the development +key by adding the ``cordappSignerKeyFingerprintBlacklist = []`` property set to empty list (see +:ref:`Configuring a node `). -Signing options can be contextually overwritten by the relevant system properties as described above. -This allows the single ``build.gradle`` file to be used for a development build (defaulting to the Corda development keystore) -and for a production build (using an external keystore). -The example system properties setup for the build process which overrides signing options: +Signing options can be contextually overwritten by the relevant system properties as described above. This allows the +single ``build.gradle`` file to be used for a development build (defaulting to the Corda development keystore) and for +a production build (using an external keystore). The example system properties setup for the build process which +overrides signing options: .. sourcecode:: shell @@ -187,8 +227,9 @@ CorDapp signing can be disabled for a build: ./gradlew -Dsigning.enabled=false -Other system properties can be explicitly assigned to options by calling ``System.getProperty`` in ``cordapp`` plugin configuration. -For example the below configuration sets the specific signing algorithm when a system property is available otherwise defaults to an empty string: +Other system properties can be explicitly assigned to options by calling ``System.getProperty`` in ``cordapp`` plugin +configuration. For example the below configuration sets the specific signing algorithm when a system property is +available otherwise defaults to an empty string: .. sourcecode:: groovy @@ -200,7 +241,8 @@ For example the below configuration sets the specific signing algorithm when a s } //... -Then the build process can set the value for *custom.sigalg* system property and other system properties recognized by ``cordapp`` plugin: +Then the build process can set the value for *custom.sigalg* system property and other system properties recognized by +``cordapp`` plugin: .. sourcecode:: shell @@ -216,8 +258,8 @@ Cordformation plugin can also sign CorDapps JARs, when deploying set of nodes, s Example ^^^^^^^ -Below is a sample of what a CorDapp's Gradle dependencies block might look like. When building your own CorDapp, you should -base yourself on the ``build.gradle`` file of the +Below is a sample of what a CorDapp's Gradle dependencies block might look like. When building your own CorDapp, you +should base yourself on the ``build.gradle`` file of the `Kotlin CorDapp Template `_ or the `Java CorDapp Template `_. diff --git a/docs/source/resources/state-hierarchy.png b/docs/source/resources/state-hierarchy.png index 232ac7d1f5..2799433431 100644 Binary files a/docs/source/resources/state-hierarchy.png and b/docs/source/resources/state-hierarchy.png differ diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 805808b761..b9ccef55c4 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -16,7 +16,7 @@ The example CorDapp allows nodes to agree IOUs with each other, as long as they We will deploy and run the CorDapp on four test nodes: -* **Notary**, which hosts a validating notary service +* **Notary**, which runs a notary service * **PartyA** * **PartyB** * **PartyC** @@ -30,10 +30,9 @@ Start by downloading the example CorDapp from GitHub: * Set up your machine by following the :doc:`quickstart guide ` -* Clone the example CorDapp from the `cordapp-example repository `_ using - the following command: ``git clone https://github.com/corda/cordapp-example`` +* Clone the samples repository from using the following command: ``git clone https://github.com/corda/samples`` -* Change directories to the freshly cloned repo: ``cd cordapp-example`` +* Change directories to the ``cordapp-example`` folder: ``cd samples/cordapp-example`` Opening the example CorDapp in IntelliJ --------------------------------------- @@ -41,7 +40,7 @@ Let's open the example CorDapp in IntelliJ IDEA: * Open IntelliJ -* A splash screen will appear. Click ``open``, select the cloned ``cordapp-example`` folder, and click ``OK`` +* A splash screen will appear. Click ``open``, navigate to and select the ``cordapp-example`` folder, and click ``OK`` * Once the project is open, click ``File``, then ``Project Structure``. Under ``Project SDK:``, set the project SDK by clicking ``New...``, clicking ``JDK``, and navigating to ``C:\Program Files\Java\jdk1.8.0_XXX`` on Windows or ``Library/Java/JavaVirtualMachines/jdk1.8.XXX`` on MacOSX (where ``XXX`` is the @@ -65,61 +64,59 @@ The example CorDapp has the following structure: │   │   └── log4j2.xml │   └── test │   └── log4j2.xml - ├── doc - │   └── example_flow.plantuml ├── gradle │   └── wrapper │   ├── gradle-wrapper.jar │   └── gradle-wrapper.properties - ├── lib - │   ├── README.txt - │   └── quasar.jar ├── java-source - │   └── ... - ├── kotlin-source │   ├── build.gradle │   └── src + │   ├── integrationTest + │   │   └── java + │   │   └── com + │   │   └── example + │   │   └── DriverBasedTests.java │   ├── main - │   │   ├── kotlin + │   │   ├── java │   │   │   └── com │   │   │   └── example │   │   │   ├── api - │   │   │   │   └── ExampleApi.kt + │   │   │   │   └── ExampleApi.java │   │   │   ├── client - │   │   │   │   └── ExampleClientRPC.kt + │   │   │   │   └── ExampleClientRPC.java │   │   │   ├── contract - │   │   │   │   └── IOUContract.kt + │   │   │   │   └── IOUContract.java │   │   │   ├── flow - │   │   │   │   └── ExampleFlow.kt - │   │   │   ├── model - │   │   │   │   └── IOU.kt + │   │   │   │   └── ExampleFlow.java │   │   │   ├── plugin - │   │   │   │   └── ExamplePlugin.kt + │   │   │   │   └── ExamplePlugin.java │   │   │   ├── schema - │   │   │   │   └── IOUSchema.kt + │   │   │   │   ├── IOUSchema.java + │   │   │   │   └── IOUSchemaV1.java │   │   │   └── state - │   │   │   └── IOUState.kt + │   │   │   └── IOUState.java │   │   └── resources │   │   ├── META-INF │   │   │   └── services │   │   │   └── net.corda.webserver.services.WebServerPluginRegistry - │   │   ├── certificates - │   │   │   ├── readme.txt - │   │   │   ├── sslkeystore.jks - │   │   │   └── truststore.jks │   │   └── exampleWeb │   │   ├── index.html │   │   └── js │   │   └── angular-module.js │   └── test - │   └── kotlin + │   └── java │   └── com │   └── example - │   ├── Main.kt + │   ├── NodeDriver.java │   ├── contract - │   │   └── IOUContractTests.kt + │   │   └── IOUContractTests.java │   └── flow - │   └── IOUFlowTests.kt + │   └── IOUFlowTests.java + ├── kotlin-source + │   ├── ... + ├── lib + │   ├── README.txt + │   └── quasar.jar ├── .gitignore ├── LICENCE ├── README.md @@ -137,16 +134,15 @@ The key files and directories are as follows: * **gradle** contains the gradle wrapper, which allows the use of Gradle without installing it yourself and worrying about which version is required * **lib** contains the Quasar jar which rewrites our CorDapp's flows to be checkpointable -* **kotlin-source** contains the source code for the example CorDapp written in Kotlin +* **java-source** contains the source code for the example CorDapp written in Java - * **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp - * **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the + * **java-source/src/main/java** contains the source code for the example CorDapp + * **java-source/src/main/resources** contains the certificate store, some static web content to be served by the nodes and the WebServerPluginRegistry file - * **kotlin-source/src/test/kotlin** contains unit tests for the contracts and flows, and the driver to run the nodes + * **java-source/src/test/java** contains unit tests for the contracts and flows, and the driver to run the nodes via IntelliJ -* **java-source** contains the same source code, but written in Java. CorDapps can be developed in any language - targeting the JVM +* **kotlin-source** contains the same source code, but written in Kotlin. CorDapps can be developed in either Java and Kotlin Running the example CorDapp --------------------------- diff --git a/experimental/corda-utils/src/main/kotlin/io/cryptoblk/core/StatusTransitions.kt b/experimental/corda-utils/src/main/kotlin/io/cryptoblk/core/StatusTransitions.kt index af7db67dfa..0d108850cf 100644 --- a/experimental/corda-utils/src/main/kotlin/io/cryptoblk/core/StatusTransitions.kt +++ b/experimental/corda-utils/src/main/kotlin/io/cryptoblk/core/StatusTransitions.kt @@ -32,14 +32,14 @@ data class PrintedTransitionGraph(val stateClassName: String, val printedPUML: S * Shorthand for defining transitions directly from the command class */ fun CommandData.txDef(signer: R? = null, from: S?, to: List): - TransitionDef = TransitionDef(this::class.java, signer, from, to) + TransitionDef = TransitionDef(this::class.java, signer, from, to) /** * For a given [stateClass] that tracks a status, it holds all possible transitions in [ts]. * This can be used for generic [verify] in contract code as well as for visualizing the state transition graph in PUML ([printGraph]). */ class StatusTransitions>(private val stateClass: KClass, - private vararg val ts: TransitionDef) { + private vararg val ts: TransitionDef) { private val allowedCmds = ts.map { it.cmd }.toSet() @@ -67,11 +67,11 @@ class StatusTransitions>(priv // for each combination of in x out which should normally be at most 1... inputStates.forEach { inp -> outputStates.forEach { outp -> - require(inp != null || outp != null) + require(inp != null || outp != null) { "Input and output states cannot be both left unspecified" } val options = matchingTransitions(inp?.status, outp?.status, cmd.value) val signerGroup = options.groupBy { it.signer }.entries.singleOrNull() - ?: throw IllegalStateException("Cannot have different signers in StatusTransitions for the same command.") + ?: throw IllegalStateException("Cannot have different signers in StatusTransitions for the same command.") val signer = signerGroup.key if (signer != null) { // which state determines who is the signer? by default the input, unless it's the initial transition diff --git a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt index e7462e69cf..ddc5af5f79 100644 --- a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt +++ b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt @@ -132,7 +132,8 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService, - cacheFactory + cacheFactory, + cordappLoader.appClassLoader ) init { diff --git a/experimental/kryo-hook/src/main/kotlin/net/corda/kryohook/KryoHook.kt b/experimental/kryo-hook/src/main/kotlin/net/corda/kryohook/KryoHook.kt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt index 7d392b70d3..b905a8c3c2 100644 --- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt +++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt @@ -92,7 +92,8 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService, - cacheFactory + cacheFactory, + cordappLoader.appClassLoader ) init { diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt index f73805b967..b5b1a97c6d 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt @@ -203,7 +203,7 @@ class UniversalContract : Contract { val rest = extractRemainder(arr, action) // for now - let's assume not - require(rest is Zero) + require(rest is Zero) { "Remainder must be zero" } requireThat { "action must have a time-window" using (tx.timeWindow != null) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt b/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt index 56cd5d497e..849cee24a8 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt @@ -321,7 +321,7 @@ open class BusinessCalendar(val holidayDates: List) { * TODO: Make more efficient if necessary */ fun moveBusinessDays(date: LocalDate, direction: DateRollDirection, i: Int): LocalDate { - require(i >= 0) + require(i >= 0){"Days to add/subtract must be positive"} if (i == 0) return date var retDate = date var ctr = 0 diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/GetBalances.kt b/finance/src/main/kotlin/net/corda/finance/contracts/GetBalances.kt index 34fb16b688..f844ac2e42 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/GetBalances.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/GetBalances.kt @@ -39,8 +39,8 @@ private fun rowsToAmount(currency: Currency, rows: Vault.Page>) return if (rows.otherResults.isEmpty()) { Amount(0L, currency) } else { - require(rows.otherResults.size == 2) - require(rows.otherResults[1] == currency.currencyCode) + require(rows.otherResults.size == 2){"Invalid number of rows returned by query"} + require(rows.otherResults[1] == currency.currencyCode){"Currency on rows returned by query does not match expected"} val quantity = rows.otherResults[0] as Long Amount(quantity, currency) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt index 046de76ee0..074cf869dd 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt @@ -298,7 +298,7 @@ abstract class OnLedgerAsset> issueCommand: CommandData): Set { check(tx.inputStates().isEmpty()) check(tx.outputStates().map { it.data }.filterIsInstance(transactionState.javaClass).isEmpty()) - require(transactionState.data.amount.quantity > 0) + require(transactionState.data.amount.quantity > 0){"Amount to issue must be greater than zero"} val at = transactionState.data.amount.token.issuer val commandSigner = at.party.owningKey tx.addOutputState(transactionState) diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 93d7430f5b..413937aaa9 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -140,8 +140,8 @@ object TwoPartyDealFlow { // Verify the transaction identities represent the correct parties val wellKnownOtherParty = serviceHub.identityService.wellKnownPartyFromAnonymous(it.primaryIdentity) val wellKnownMe = serviceHub.identityService.wellKnownPartyFromAnonymous(it.secondaryIdentity) - require(wellKnownOtherParty == otherSideSession.counterparty) - require(wellKnownMe == ourIdentity) + require(wellKnownOtherParty == otherSideSession.counterparty){"Well known party for handshake identity ${it.primaryIdentity} does not match counterparty"} + require(wellKnownMe == ourIdentity){"Well known party for handshake identity ${it.secondaryIdentity} does not match ourIdentity"} validateHandshake(it) } } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 57c79cf191..92ab856e69 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -203,7 +203,7 @@ object TwoPartyTradeFlow { // The asset must either be owned by the well known identity of the counterparty, or we must be able to // prove the owner is a confidential identity of the counterparty. val assetForSaleIdentity = serviceHub.identityService.wellKnownPartyFromAnonymous(asset.owner) - require(assetForSaleIdentity == sellerSession.counterparty) + require(assetForSaleIdentity == sellerSession.counterparty){"Well known identity lookup returned identity that does not match counterparty"} // Register the identity we're about to send payment to. This shouldn't be the same as the asset owner // identity, so that anonymity is enforced. diff --git a/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt b/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt index 5277921bd1..0e54a2ffba 100644 --- a/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt +++ b/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt @@ -9,8 +9,8 @@ data class ScreenCoordinate(val screenX: Double, val screenY: Double) @CordaSerializable data class WorldCoordinate(val latitude: Double, val longitude: Double) { init { - require(latitude in -90..90) - require(longitude in -180..180) + require(latitude in -90..90){"Latitude must be between -90 and +90"} + require(longitude in -180..180){"Longitude must be between -180 and +180"} } /** @@ -24,8 +24,8 @@ data class WorldCoordinate(val latitude: Double, val longitude: Double) { @Suppress("unused") // Used from the visualiser GUI. fun project(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double, leftLongitude: Double, rightLongitude: Double): ScreenCoordinate { - require(latitude in bottomLatitude..topLatitude) - require(longitude in leftLongitude..rightLongitude) + require(latitude in bottomLatitude..topLatitude){"Latitude must be between $bottomLatitude and $topLatitude"} + require(longitude in leftLongitude..rightLongitude){"Longitude must be between $leftLongitude and $rightLongitude"} fun deg2rad(deg: Double) = deg * Math.PI / 180.0 val leftLngRad = deg2rad(leftLongitude) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index e9e1ebf0d4..4b1bd358ef 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -25,6 +25,7 @@ import net.corda.testing.contracts.DummyState import net.corda.testing.core.* import net.corda.testing.dsl.* import net.corda.testing.internal.TEST_TX_TIME +import net.corda.testing.internal.fakeAttachment import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.vault.CommodityState import net.corda.testing.node.MockServices @@ -565,7 +566,7 @@ class ObligationTests { @Test fun `commodity settlement`() { - val commodityContractBytes = "https://www.big-book-of-banking-law.gov/commodity-claims.html".toByteArray() + val commodityContractBytes = fakeAttachment("file1.txt", "https://www.big-book-of-banking-law.gov/commodity-claims.html") val defaultFcoj = Issued(defaultIssuer, Commodity.getInstance("FCOJ")!!) val oneUnitFcoj = Amount(1, defaultFcoj) val obligationDef = Obligation.Terms(NonEmptySet.of(commodityContractBytes.sha256() as SecureHash), NonEmptySet.of(defaultFcoj), TEST_TX_TIME) @@ -957,7 +958,7 @@ class ObligationTests { assertEquals(expected, actual) } - private val cashContractBytes = "https://www.big-book-of-banking-law.gov/cash-claims.html".toByteArray() + private val cashContractBytes = fakeAttachment("file1.txt", "https://www.big-book-of-banking-law.gov/cash-claims.html") private val Issued.OBLIGATION_DEF: Obligation.Terms get() = Obligation.Terms(NonEmptySet.of(cashContractBytes.sha256() as SecureHash), NonEmptySet.of(this), TEST_TX_TIME) private val Amount>.OBLIGATION: Obligation.State diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt index d08cd750cb..a4c352fcca 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DevIdentityGenerator.kt @@ -47,7 +47,7 @@ object DevIdentityGenerator { /** Generates a CFT notary identity, where the entire cluster shares a key pair. */ fun generateDistributedNotarySingularIdentity(dirs: List, notaryName: CordaX500Name): Party { - require(dirs.isNotEmpty()) + require(dirs.isNotEmpty()){"At least one directory to generate identity for must be specified"} log.trace { "Generating singular identity \"$notaryName\" for nodes: ${dirs.joinToString()}" } @@ -63,7 +63,7 @@ object DevIdentityGenerator { /** Generates a BFT notary identity: individual key pairs for each cluster member, and a shared composite key. */ fun generateDistributedNotaryCompositeIdentity(dirs: List, notaryName: CordaX500Name, threshold: Int = 1): Party { - require(dirs.isNotEmpty()) + require(dirs.isNotEmpty()){"At least one directory to generate identity for must be specified"} log.trace { "Generating composite identity \"$notaryName\" for nodes: ${dirs.joinToString()}" } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 9a21bfac72..87618d4e52 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -254,7 +254,7 @@ object X509Utilities { crlIssuer: X500Name? = null): X509Certificate { val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer) return builder.build(issuerSigner).run { - require(isValidOn(Date())) + require(isValidOn(Date())){"Certificate is not valid at instant now"} toJca() } } @@ -292,8 +292,8 @@ object X509Utilities { crlDistPoint, crlIssuer) return builder.build(signer).run { - require(isValidOn(Date())) - require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))) + require(isValidOn(Date())){"Certificate is not valid at instant now"} + require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))){"Invalid signature"} toJca() } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index 52138854a4..36c567107c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -431,10 +431,10 @@ internal constructor(private val initSerEnv: Boolean, private fun NodeInfo.notaryIdentity(): Party { return when (legalIdentities.size) { - // Single node notaries have just one identity like all other nodes. This identity is the notary identity + // Single node notaries have just one identity like all other nodes. This identity is the notary identity 1 -> legalIdentities[0] - // Nodes which are part of a distributed notary have a second identity which is the composite identity of the - // cluster and is shared by all the other members. This is the notary identity. + // Nodes which are part of a distributed notary have a second identity which is the composite identity of the + // cluster and is shared by all the other members. This is the notary identity. 2 -> legalIdentities[1] else -> throw IllegalArgumentException("Not sure how to get the notary identity in this scenerio: $this") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt index 86c3266bb6..520c9cabfe 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt @@ -2,7 +2,7 @@ package net.corda.nodeapi.internal.network import net.corda.core.internal.* import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import rx.Observable import rx.Scheduler import rx.Subscription diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index d8a82a6ff1..41d9f53435 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -64,7 +64,8 @@ class CordaPersistence( databaseConfig: DatabaseConfig, schemas: Set, cacheFactory: NamedCacheFactory, - attributeConverters: Collection> = emptySet() + attributeConverters: Collection> = emptySet(), + customClassLoader: ClassLoader? = null ) : Closeable { companion object { private val log = contextLogger() @@ -73,7 +74,7 @@ class CordaPersistence( private val defaultIsolationLevel = databaseConfig.transactionIsolationLevel val hibernateConfig: HibernateConfiguration by lazy { transaction { - HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory) + HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cacheFactory, customClassLoader) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt index 679be4df8d..46fb5eab99 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt @@ -32,7 +32,7 @@ class HibernateConfiguration( private val attributeConverters: Collection>, private val jdbcUrl: String, cacheFactory: NamedCacheFactory, - val cordappClassLoader: ClassLoader? = null + val customClassLoader: ClassLoader? = null ) { companion object { private val logger = contextLogger() @@ -104,7 +104,7 @@ class HibernateConfiguration( schema.mappedTypes.forEach { config.addAnnotatedClass(it) } } - val sessionFactory = buildSessionFactory(config, metadataSources, cordappClassLoader) + val sessionFactory = buildSessionFactory(config, metadataSources, customClassLoader) logger.info("Created session factory for schemas: $schemas") // export Hibernate JMX statistics @@ -130,13 +130,13 @@ class HibernateConfiguration( } } - private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, cordappClassLoader: ClassLoader?): SessionFactory { + private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, customClassLoader: ClassLoader?): SessionFactory { config.standardServiceRegistryBuilder.applySettings(config.properties) - if (cordappClassLoader != null) { + if (customClassLoader != null) { config.standardServiceRegistryBuilder.addService( ClassLoaderService::class.java, - ClassLoaderServiceImpl(cordappClassLoader)) + ClassLoaderServiceImpl(customClassLoader)) } val metadataBuilder = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build()) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt index 0c1b439d99..0c2345db05 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt @@ -11,7 +11,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.node.services.config.NotaryConfig import net.corda.nodeapi.internal.DEV_ROOT_CA -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.toConfig diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt index abacb8a0e3..97e21787bb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt @@ -4,7 +4,7 @@ import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.internal.write import net.corda.nodeapi.eventually -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Rule diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 4fe4b47650..8f527468e5 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -5,10 +5,11 @@ import net.corda.core.internal.* import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.ParametersUpdateInfo import net.corda.core.node.NodeInfo +import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME import net.corda.nodeapi.internal.network.SignedNetworkParameters @@ -24,14 +25,13 @@ import net.corda.testing.node.internal.* import net.corda.testing.node.internal.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy +import org.hamcrest.CoreMatchers.`is` import org.junit.* import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Rule -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import java.net.URL +import java.nio.file.Files import java.time.Instant @RunWith(Parameterized::class) @@ -247,7 +247,9 @@ class NetworkMapTest(var initFunc: (URL, NetworkMapServer) -> CompatibilityZoneP // Make sure the nodes aren't getting the node infos from their additional directories val nodeInfosDir = baseDirectory / NODE_INFO_DIRECTORY if (nodeInfosDir.exists()) { - assertThat(nodeInfosDir.list()).isEmpty() + Assert.assertThat(nodeInfosDir.list().size, `is`(1)) + Assert.assertThat(Files.readAllBytes(nodeInfosDir.list().single()).deserialize().verified().legalIdentities.first(), `is`( this.nodeInfo.legalIdentities.first())) + } assertThat(rpc.networkMapSnapshot()).containsOnly(*nodes) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index f65aa1e5c5..54fb67c7c8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -77,10 +77,10 @@ class LargeTransactionsTest : IntegrationTest() { fun checkCanSendLargeTransactions() { // These 4 attachments yield a transaction that's got >10mb attached, so it'd push us over the Artemis // max message size. - val bigFile1 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 0) - val bigFile2 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 1) - val bigFile3 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 2) - val bigFile4 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 3) + val bigFile1 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 0, "a") + val bigFile2 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 1, "b") + val bigFile3 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 2, "c") + val bigFile4 = InputStreamAndHash.createInMemoryTestZip(3.MB.toInt(), 3, "d") driver(DriverParameters( startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts"), diff --git a/node/src/main/java/CordaCaplet.java b/node/src/main/java/CordaCaplet.java index bcddb017be..2c6aa49799 100644 --- a/node/src/main/java/CordaCaplet.java +++ b/node/src/main/java/CordaCaplet.java @@ -128,7 +128,6 @@ public class CordaCaplet extends Capsule { } // Add additional directories of JARs to the classpath (at the end), e.g., for JDBC drivers. augmentClasspath(cp, new File(baseDir, "drivers")); - augmentClasspath(cp, cordappsDir); try { List jarDirs = nodeConfig.getStringList("jarDirs"); log(LOG_VERBOSE, "Configured JAR directories = " + jarDirs); diff --git a/node/src/main/kotlin/net/corda/node/NodeCmdLineOptions.kt b/node/src/main/kotlin/net/corda/node/NodeCmdLineOptions.kt index 1141c20297..37293ddf51 100644 --- a/node/src/main/kotlin/net/corda/node/NodeCmdLineOptions.kt +++ b/node/src/main/kotlin/net/corda/node/NodeCmdLineOptions.kt @@ -75,7 +75,7 @@ open class SharedNodeCmdLineOptions { errors.forEach { error -> when (error) { is ConfigException.IO -> logger.error(configFileNotFoundMessage(configFile)) - else -> logger.error(error.message, error) + else -> logger.error(error.message) } } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 41007cbd0a..22c5785519 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -154,7 +154,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService, - cacheFactory) + cacheFactory, + this.cordappLoader.appClassLoader) init { // TODO Break cyclic dependency @@ -466,6 +467,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, // Write the node-info file even if nothing's changed, just in case the file has been deleted. NodeInfoWatcher.saveToFile(configuration.baseDirectory, nodeInfoAndSigned) + NodeInfoWatcher.saveToFile(configuration.baseDirectory / NODE_INFO_DIRECTORY, nodeInfoAndSigned) // Always republish on startup, it's treated by network map server as a heartbeat. if (publish && networkMapClient != null) { @@ -774,7 +776,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.") val isH2Database = isH2Database(props.getProperty("dataSource.url", "")) val schemas = if (isH2Database) schemaService.internalSchemas() else schemaService.schemaOptions.keys - database.startHikariPool(props, configuration.database, schemas, metricRegistry) + database.startHikariPool(props, configuration.database, schemas, metricRegistry, this.cordappLoader.appClassLoader) // Now log the vendor string as this will also cause a connection to be tested eagerly. logVendorString(database, log) } @@ -1093,21 +1095,22 @@ fun createCordaPersistence(databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -> Party?, wellKnownPartyFromAnonymous: (AbstractParty) -> Party?, schemaService: SchemaService, - cacheFactory: NamedCacheFactory): CordaPersistence { + cacheFactory: NamedCacheFactory, + customClassLoader: ClassLoader?): CordaPersistence { // Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately // Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default // so we end up providing both descriptor and converter. We should re-examine this in later versions to see if // either Hibernate can be convinced to stop warning, use the descriptor by default, or something else. JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)) val attributeConverters = listOf(PublicKeyToTextConverter(), AbstractPartyToX500NameAsStringConverter(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)) - return CordaPersistence(databaseConfig, schemaService.schemaOptions.keys, cacheFactory, attributeConverters) + return CordaPersistence(databaseConfig, schemaService.schemaOptions.keys, cacheFactory, attributeConverters, customClassLoader) } -fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set, metricRegistry: MetricRegistry? = null) { +fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set, metricRegistry: MetricRegistry? = null, classloader: ClassLoader = Thread.currentThread().contextClassLoader) { try { val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry) val jdbcUrl = hikariProperties.getProperty("dataSource.url", "") - val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig) + val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, classloader) schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L }, isH2Database(jdbcUrl)) start(dataSource, jdbcUrl) } catch (ex: Exception) { diff --git a/node/src/main/kotlin/net/corda/node/internal/artemis/ReactiveArtemisConsumer.kt b/node/src/main/kotlin/net/corda/node/internal/artemis/ReactiveArtemisConsumer.kt index f7adb54ca7..0c0eb71f9f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/artemis/ReactiveArtemisConsumer.kt +++ b/node/src/main/kotlin/net/corda/node/internal/artemis/ReactiveArtemisConsumer.kt @@ -39,7 +39,7 @@ private class MultiplexingReactiveArtemisConsumer(private val queueNames: Set createSession().apply { start() diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt index e3ff2584f7..87183504e8 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt @@ -11,7 +11,7 @@ import net.corda.core.internal.writer import net.corda.core.serialization.internal.CheckpointSerializationContext import net.corda.core.serialization.ClassWhitelist import net.corda.core.utilities.contextLogger -import net.corda.serialization.internal.AttachmentsClassLoader +import net.corda.core.serialization.internal.AttachmentsClassLoader import net.corda.serialization.internal.MutableClassWhitelist import net.corda.serialization.internal.TransientClassWhiteList import net.corda.serialization.internal.amqp.hasCordaSerializable diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt b/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt index 0384a6cc56..e0fe33fb41 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt @@ -77,7 +77,9 @@ class ImmutableClassSerializer(val klass: KClass) : Serializer() // Verify that this class is immutable (all properties are final). // We disable this check inside SGX as the reflection blows up. if (!SgxSupport.isInsideEnclave) { - require(props.none { it is KMutableProperty<*> }) + props.forEach { + require(it !is KMutableProperty<*>) { "$it mutable property of class: ${klass} is unsupported" } + } } } diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index dcd1ab7b31..18c1830c8d 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -284,8 +284,8 @@ data class SecurityConfiguration(val authService: SecurityConfiguration.AuthServ val users: List? = null) { init { when (type) { - AuthDataSourceType.INMEMORY -> require(users != null && connection == null) - AuthDataSourceType.DB -> require(users == null && connection != null) + AuthDataSourceType.INMEMORY -> require(users != null && connection == null) { "In-memory authentication must specify a user list, and must not configure a database" } + AuthDataSourceType.DB -> require(users == null && connection != null) { "Database-backed authentication must not specify a user list, and must configure a database" } } } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index 5545daa88b..b4c80817db 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -673,7 +673,7 @@ private class P2PMessagingConsumer( override fun start() { synchronized(this) { - require(!startedFlag) + require(!startedFlag){"Must not already be started"} drainingModeWasChangedEvents.filter { change -> change.switchedOn() }.doOnNext { initialAndExistingConsumer.switchTo(existingOnlyConsumer) }.subscribe() drainingModeWasChangedEvents.filter { change -> change.switchedOff() }.doOnNext { existingOnlyConsumer.switchTo(initialAndExistingConsumer) }.subscribe() subscriptions += existingOnlyConsumer.messages.doOnNext(messages::onNext).subscribe() diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index ee833dbb1a..4a3dd79427 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -289,7 +289,7 @@ class RPCServer( private fun bindingRemovalArtemisMessageHandler(artemisMessage: ClientMessage) { lifeCycle.requireState(State.STARTED) val notificationType = artemisMessage.getStringProperty(ManagementHelper.HDR_NOTIFICATION_TYPE) - require(notificationType == CoreNotificationType.BINDING_REMOVED.name) + require(notificationType == CoreNotificationType.BINDING_REMOVED.name){"Message contained notification type of $notificationType instead of expected ${CoreNotificationType.BINDING_REMOVED.name}"} val clientAddress = artemisMessage.getStringProperty(ManagementHelper.HDR_ROUTING_NAME) log.warn("Detected RPC client disconnect on address $clientAddress, scheduling for reaping") invalidateClient(SimpleString(clientAddress)) @@ -299,7 +299,7 @@ class RPCServer( private fun bindingAdditionArtemisMessageHandler(artemisMessage: ClientMessage) { lifeCycle.requireState(State.STARTED) val notificationType = artemisMessage.getStringProperty(ManagementHelper.HDR_NOTIFICATION_TYPE) - require(notificationType == CoreNotificationType.BINDING_ADDED.name) + require(notificationType == CoreNotificationType.BINDING_ADDED.name){"Message contained notification type of $notificationType instead of expected ${CoreNotificationType.BINDING_ADDED.name}"} val clientAddress = SimpleString(artemisMessage.getStringProperty(ManagementHelper.HDR_ROUTING_NAME)) log.debug("RPC client queue created on address $clientAddress") diff --git a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt index 814e193b25..ee0b3b068f 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt @@ -7,7 +7,7 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.core.utilities.seconds -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import rx.Observable diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 37f5a38d6c..88be7c6e8b 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -229,7 +229,7 @@ class NodeAttachmentService( val attachmentImpl = AttachmentImpl(id, { attachment.content }, checkAttachmentsOnLoad).let { val contracts = attachment.contractClassNames if (contracts != null && contracts.isNotEmpty()) { - ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader, attachment.signers + ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader, attachment.signers?.toList() ?: emptyList()) } else { it @@ -300,7 +300,7 @@ class NodeAttachmentService( private fun import(jar: InputStream, uploader: String?, filename: String?): AttachmentId { return database.transaction { withContractsInJar(jar) { contractClassNames, inputStream -> - require(inputStream !is JarInputStream) + require(inputStream !is JarInputStream){"Input stream must not be a JarInputStream"} // Read the file into RAM and then calculate its hash. The attachment must fit into memory. // TODO: Switch to a two-phase insert so we can handle attachments larger than RAM. diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/CountUpDownLatch.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/CountUpDownLatch.kt index 995ee4b2df..7db19406d6 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/CountUpDownLatch.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/CountUpDownLatch.kt @@ -1,7 +1,7 @@ package net.corda.node.services.statemachine -import co.paralleluniverse.strands.concurrent.AbstractQueuedSynchronizer import co.paralleluniverse.fibers.Suspendable +import co.paralleluniverse.strands.concurrent.AbstractQueuedSynchronizer /** * Quasar-compatible latch that may be incremented. @@ -56,7 +56,7 @@ class CountUpDownLatch(initialValue: Int) { } fun countDown(number: Int = 1) { - require(number > 0) + require(number > 0){"Number to count down by must be greater than 0"} sync.releaseShared(number) } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index d9e81e2252..591e9e7762 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -197,7 +197,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, "Transaction context is missing. This might happen if a suspendable method is not annotated with @Suspendable annotation." } } else { - require(contextTransactionOrNull == null) + require(contextTransactionOrNull == null){"Transaction is marked as not present, but is not null"} } } @@ -392,7 +392,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, isDbTransactionOpenOnEntry = true, isDbTransactionOpenOnExit = false ) - require(continuation == FlowContinuation.ProcessEvents) + require(continuation == FlowContinuation.ProcessEvents){"Expected a continuation of type ${FlowContinuation.ProcessEvents}, found $continuation "} unpark(SERIALIZER_BLOCKER) } return uncheckedCast(processEventsUntilFlowIsResumed( diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt index 19e1284f83..1facf8d478 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt @@ -172,7 +172,7 @@ class SingleThreadedStateMachineManager( * @param allowedUnsuspendedFiberCount Optional parameter is used in some tests. */ override fun stop(allowedUnsuspendedFiberCount: Int) { - require(allowedUnsuspendedFiberCount >= 0) + require(allowedUnsuspendedFiberCount >= 0){"allowedUnsuspendedFiberCount must be greater than or equal to zero"} mutex.locked { if (stopping) throw IllegalStateException("Already stopping!") stopping = true @@ -775,10 +775,10 @@ class SingleThreadedStateMachineManager( ) { drainFlowEventQueue(flow) // final sanity checks - require(lastState.pendingDeduplicationHandlers.isEmpty()) - require(lastState.isRemoved) - require(lastState.checkpoint.subFlowStack.size == 1) - require(flow.fiber.id !in sessionToFlow.values) + require(lastState.pendingDeduplicationHandlers.isEmpty()) { "Flow cannot be removed until all pending deduplications have completed" } + require(lastState.isRemoved) { "Flow must be in removable state before removal" } + require(lastState.checkpoint.subFlowStack.size == 1) { "Checkpointed stack must be empty" } + require(flow.fiber.id !in sessionToFlow.values) { "Flow fibre must not be needed by an existing session" } flow.resultFuture.set(removalReason.flowReturnValue) lastState.flowLogic.progressTracker?.currentStep = ProgressTracker.DONE changesPublisher.onNext(StateMachineManager.Change.Removed(lastState.flowLogic, Try.Success(removalReason.flowReturnValue))) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/FiberDeserializationCheckingInterceptor.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/FiberDeserializationCheckingInterceptor.kt index 463d5bf10c..7e6aaa52bd 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/FiberDeserializationCheckingInterceptor.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/FiberDeserializationCheckingInterceptor.kt @@ -2,17 +2,11 @@ package net.corda.node.services.statemachine.interceptors import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.StateMachineRunId -import net.corda.core.serialization.* +import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.internal.CheckpointSerializationContext import net.corda.core.serialization.internal.checkpointDeserialize import net.corda.core.utilities.contextLogger -import net.corda.node.services.statemachine.ActionExecutor -import net.corda.node.services.statemachine.Event -import net.corda.node.services.statemachine.FlowFiber -import net.corda.node.services.statemachine.FlowState -import net.corda.node.services.statemachine.FlowStateMachineImpl -import net.corda.node.services.statemachine.StateMachineState -import net.corda.node.services.statemachine.TransitionExecutor +import net.corda.node.services.statemachine.* import net.corda.node.services.statemachine.transitions.FlowContinuation import net.corda.node.services.statemachine.transitions.TransitionResult import java.util.concurrent.LinkedBlockingQueue @@ -69,7 +63,7 @@ class FiberDeserializationChecker { private var foundUnrestorableFibers: Boolean = false fun start(checkpointSerializationContext: CheckpointSerializationContext) { - require(checkerThread == null) + require(checkerThread == null){"Checking thread must not already be started"} checkerThread = thread(name = "FiberDeserializationChecker") { while (true) { val job = jobQueue.take() diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 6c636d9e74..24562519dc 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -372,7 +372,7 @@ class NodeRegistrationHelper( private class FixedPeriodLimitedRetrialStrategy(times: Int, private val period: Duration) : (Duration?) -> Duration? { init { - require(times > 0) + require(times > 0){"Retry attempts must be larger than zero"} } private var counter = times diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index 5cf1aa2b02..bbe4812c8d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -16,7 +16,7 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.millis import net.corda.node.VersionInfo import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.network.NETWORK_PARAMS_UPDATE_FILE_NAME import net.corda.nodeapi.internal.network.NodeInfoFilesCopier diff --git a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index 034afa9121..8a02e9e86f 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -6,7 +6,7 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.size import net.corda.core.node.services.KeyManagementService -import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY +import net.corda.core.internal.NODE_INFO_DIRECTORY import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.testing.core.ALICE_NAME diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt index fb45eb86dd..f1e6a835a5 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt @@ -31,7 +31,7 @@ class SchemaMigrationTest { private fun configureDatabase(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemaService: NodeSchemaService = NodeSchemaService()): CordaPersistence = - createCordaPersistence(databaseConfig, { null }, { null }, schemaService, TestingNamedCacheFactory()) + createCordaPersistence(databaseConfig, { null }, { null }, schemaService, TestingNamedCacheFactory(), null) .apply { startHikariPool(hikariProperties, databaseConfig, schemaService.schemaOptions.keys) } @Test diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt index 28db2d7d13..19d8693539 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt @@ -54,10 +54,10 @@ class MaxTransactionSizeTests { @Test fun `check transaction will fail when exceed max transaction size limit`() { // These 4 attachments yield a transaction that's got ~ 4mb, which will exceed the 3mb max transaction size limit - val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0) - val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1) - val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2) - val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3) + val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0, "a") + val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1, "b") + val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2, "c") + val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3, "d") val flow = aliceNode.transaction { val hash1 = aliceNode.importAttachment(bigFile1.inputStream) val hash2 = aliceNode.importAttachment(bigFile2.inputStream) @@ -77,10 +77,10 @@ class MaxTransactionSizeTests { @Test fun `check transaction will be rejected by counterparty when exceed max transaction size limit`() { // These 4 attachments yield a transaction that's got ~ 4mb, which will exceed the 3mb max transaction size limit - val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0) - val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1) - val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2) - val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3) + val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0, "a") + val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1, "b") + val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2, "c") + val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3, "c") val flow = aliceNode.transaction { val hash1 = aliceNode.importAttachment(bigFile1.inputStream) val hash2 = aliceNode.importAttachment(bigFile2.inputStream) diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 08a4fa3873..c32217c429 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -94,7 +94,7 @@ private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash. val id = rpc.uploadAttachment(it) require(hash == id) { "Id was '$id' instead of '$hash'" } } - require(rpc.attachmentExists(hash)) + require(rpc.attachmentExists(hash)){"Attachment matching hash: $hash does not exist"} } val flowHandle = rpc.startTrackedFlow(::AttachmentDemoFlow, otherSideFuture.get(), notaryFuture.get(), hash) @@ -159,7 +159,7 @@ fun recipient(rpc: CordaRPCOps, webPort: Int) { if (wtx.attachments.isNotEmpty()) { if (wtx.outputs.isNotEmpty()) { val state = wtx.outputsOfType().single() - require(rpc.attachmentExists(state.hash)) + require(rpc.attachmentExists(state.hash)) {"attachment matching hash: ${state.hash} does not exist"} // Download the attachment via the Web endpoint. val connection = URL("http://localhost:$webPort/attachments/${state.hash}").openConnection() as HttpURLConnection @@ -207,7 +207,7 @@ class AttachmentContract : Contract { override fun verify(tx: LedgerTransaction) { val state = tx.outputsOfType().single() // we check that at least one has the matching hash, the other will be the contract - require(tx.attachments.any { it.id == state.hash }) + require(tx.attachments.any { it.id == state.hash }) {"At least one attachment in transaction must match hash ${state.hash}"} } object Command : TypeOnlyCommandData() diff --git a/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index ac96e95d38..119bc683e4 100644 --- a/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -40,7 +40,7 @@ data class PortfolioState(val portfolio: List, } override fun generateRevision(notary: Party, oldState: StateAndRef<*>, updatedValue: Update): TransactionBuilder { - require(oldState.state.data == this) + require(oldState.state.data == this){"Old state data does not match current state data"} val portfolio = updatedValue.portfolio ?: portfolio val valuation = updatedValue.valuation ?: valuation diff --git a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderBuilder.kt b/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderBuilder.kt deleted file mode 100644 index 7f77351952..0000000000 --- a/serialization-deterministic/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderBuilder.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.corda.serialization.internal - -import net.corda.core.crypto.SecureHash - -/** - * Drop-in replacement for [AttachmentsClassLoaderBuilder] in the serialization module. - * This version is not strongly-coupled to [net.corda.core.node.ServiceHub]. - */ -@Suppress("UNUSED", "UNUSED_PARAMETER") -internal class AttachmentsClassLoaderBuilder() { - fun build(attachmentHashes: List, properties: Map, deserializationClassLoader: ClassLoader): AttachmentsClassLoader? = null -} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoader.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoader.kt deleted file mode 100644 index 79de2f342b..0000000000 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoader.kt +++ /dev/null @@ -1,115 +0,0 @@ -package net.corda.serialization.internal - -import net.corda.core.KeepForDJVM -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.ContractAttachment -import net.corda.core.crypto.SecureHash -import net.corda.core.internal.isUploaderTrusted -import net.corda.core.serialization.CordaSerializable -import java.io.ByteArrayOutputStream -import java.io.FileNotFoundException -import java.io.InputStream -import java.net.URL -import java.net.URLConnection -import java.net.URLStreamHandler -import java.security.CodeSigner -import java.security.CodeSource -import java.security.SecureClassLoader -import java.util.* - -/** - * A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only - * need to provide JAR streams, and so could be fetched from a database, local disk, etc. Constructing an - * AttachmentsClassLoader is somewhat expensive, as every attachment is scanned to ensure that there are no overlapping - * file paths. - */ -@KeepForDJVM -class AttachmentsClassLoader(attachments: List, parent: ClassLoader = ClassLoader.getSystemClassLoader()) : SecureClassLoader(parent) { - private val pathsToAttachments = HashMap() - private val idsToAttachments = HashMap() - - @CordaSerializable - class OverlappingAttachments(val path: String) : Exception() { - override fun toString() = "Multiple attachments define a file at path $path" - } - - init { - require(attachments.mapNotNull { it as? ContractAttachment }.all { isUploaderTrusted(it.uploader) }) { - "Attempting to load Contract Attachments downloaded from the network" - } - - for (attachment in attachments) { - attachment.openAsJAR().use { jar -> - while (true) { - val entry = jar.nextJarEntry ?: break - - // We already verified that paths are not strange/game playing when we inserted the attachment - // into the storage service. So we don't need to repeat it here. - // - // We forbid files that differ only in case, or path separator to avoid issues for Windows/Mac developers where the - // filesystem tries to be case insensitive. This may break developers who attempt to use ProGuard. - // - // Also convert to Unix path separators as all resource/class lookups will expect this. - val path = entry.name.toLowerCase().replace('\\', '/') - if (path in pathsToAttachments) - throw OverlappingAttachments(path) - pathsToAttachments[path] = attachment - } - } - idsToAttachments[attachment.id] = attachment - } - } - - // Example: attachment://0b4fc1327f3bbebf1bfe98330ea402ae035936c3cb6da9bd3e26eeaa9584e74d/some/file.txt - // - // We have to provide a fake stream handler to satisfy the URL class that the scheme is known. But it's not - // a real scheme and we don't register it. It's just here to ensure that there aren't codepaths that could - // lead to data loading that we don't control right here in this class (URLs can have evil security properties!) - private val fakeStreamHandler = object : URLStreamHandler() { - override fun openConnection(u: URL?): URLConnection? { - throw UnsupportedOperationException() - } - } - - private fun Attachment.toURL(path: String?) = URL(null, "attachment://$id/" + (path ?: ""), fakeStreamHandler) - - override fun findClass(name: String): Class<*> { - val path = name.replace('.', '/').toLowerCase() + ".class" - val attachment = pathsToAttachments[path] ?: throw ClassNotFoundException(name) - val stream = ByteArrayOutputStream() - try { - attachment.extractFile(path, stream) - } catch (e: FileNotFoundException) { - throw ClassNotFoundException(name) - } - val bytes = stream.toByteArray() - // We don't attempt to propagate signatures from the JAR into the codesource, because our sandbox does not - // depend on external policy files to specify what it can do, so the data wouldn't be useful. - val codesource = CodeSource(attachment.toURL(null), emptyArray()) - // TODO: Define an empty ProtectionDomain to start enforcing the standard Java sandbox. - // The standard Java sandbox is insufficient for our needs and a much more sophisticated sandboxing - // ClassLoader will appear here in future, but it can't hurt to use the default one too: defence in depth! - return defineClass(name, bytes, 0, bytes.size, codesource) - } - - override fun findResource(name: String): URL? { - val attachment = pathsToAttachments[name.toLowerCase()] ?: return null - return attachment.toURL(name) - } - - override fun getResourceAsStream(name: String): InputStream? { - val url = getResource(name) ?: return null // May check parent classloaders, for example. - if (url.protocol != "attachment") return null - val attachment = idsToAttachments[SecureHash.parse(url.host)] ?: return null - val path = url.path?.substring(1) ?: return null // Chop off the leading slash. - return try { - val stream = ByteArrayOutputStream() - attachment.extractFile(path, stream) - stream.toByteArray().inputStream() - } catch (e: FileNotFoundException) { - null - } - } -} - - diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt index 12519312e9..83ec6971ce 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/CheckpointSerializationScheme.kt @@ -16,15 +16,6 @@ data class CheckpointSerializationContextImpl @JvmOverloads constructor( override val objectReferencesEnabled: Boolean, override val encoding: SerializationEncoding?, override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist) : CheckpointSerializationContext { - /** - * {@inheritDoc} - * - * Unsupported for checkpoints. - */ - override fun withAttachmentsClassLoader(attachmentHashes: List): CheckpointSerializationContext { - throw UnsupportedOperationException() - } - override fun withProperty(property: Any, value: Any): CheckpointSerializationContext { return copy(properties = properties + (property to value)) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt index ed5aecd987..5f4b1127c3 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt @@ -8,6 +8,7 @@ import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash import net.corda.core.internal.copyBytes import net.corda.core.serialization.* +import net.corda.core.serialization.internal.AttachmentsClassLoader import net.corda.core.utilities.ByteSequence import net.corda.serialization.internal.amqp.amqpMagic import org.slf4j.LoggerFactory @@ -31,20 +32,12 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override val useCase: SerializationContext.UseCase, override val encoding: SerializationEncoding?, override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist, - override val lenientCarpenterEnabled: Boolean = false, - private val builder: AttachmentsClassLoaderBuilder = AttachmentsClassLoaderBuilder() -) : SerializationContext { - - + override val lenientCarpenterEnabled: Boolean = false) : SerializationContext { /** * {@inheritDoc} - * - * We need to cache the AttachmentClassLoaders to avoid too many contexts, since the class loader is part of cache key for the context. */ override fun withAttachmentsClassLoader(attachmentHashes: List): SerializationContext { - properties[attachmentsClassLoaderEnabledPropertyName] as? Boolean == true || return this - val classLoader = builder.build(attachmentHashes, properties, deserializationClassLoader) ?: return this - return withClassLoader(classLoader) + return this } override fun withProperty(property: Any, value: Any): SerializationContext { @@ -72,34 +65,6 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override fun withEncodingWhitelist(encodingWhitelist: EncodingWhitelist) = copy(encodingWhitelist = encodingWhitelist) } -/* - * This class is internal rather than private so that serialization-deterministic - * can replace it with an alternative version. - */ -@DeleteForDJVM -class AttachmentsClassLoaderBuilder() { - private val cache: Cache, ClassLoader>, AttachmentsClassLoader> = Caffeine.newBuilder().weakValues().maximumSize(1024).build() - - fun build(attachmentHashes: List, properties: Map, deserializationClassLoader: ClassLoader): AttachmentsClassLoader? { - val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContext ?: return null // Some tests don't set one. - try { - return cache.get(Pair(attachmentHashes, deserializationClassLoader)) { - val missing = ArrayList() - val attachments = ArrayList() - attachmentHashes.forEach { id -> - serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } - ?: run { missing += id } - } - missing.isNotEmpty() && throw MissingAttachmentsException(missing) - AttachmentsClassLoader(attachments, parent = deserializationClassLoader) - }!! - } catch (e: ExecutionException) { - // Caught from within the cache get, so unwrap. - throw e.cause!! - } - } -} - @KeepForDJVM open class SerializationFactoryImpl( // TODO: This is read-mostly. Probably a faster implementation to be found. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt new file mode 100644 index 0000000000..f717f87f3b --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModel.kt @@ -0,0 +1,209 @@ +package net.corda.serialization.internal.amqp + +import net.corda.serialization.internal.model.* +import java.io.NotSerializableException +import java.util.* + +/** + * Interprets AMQP [Schema] information to obtain [RemoteTypeInformation], caching by [TypeDescriptor]. + */ +class AMQPRemoteTypeModel { + + private val cache: MutableMap = DefaultCacheProvider.createCache() + + /** + * Interpret a [Schema] to obtain a [Map] of all of the [RemoteTypeInformation] contained therein, indexed by + * [TypeDescriptor]. + * + * A [Schema] contains a set of [TypeNotation]s, which we recursively convert into [RemoteTypeInformation], + * associating each new piece of [RemoteTypeInformation] with the [TypeDescriptor] attached to it in the schema. + * + * We start by building a [Map] of [TypeNotation] by [TypeIdentifier], using [AMQPTypeIdentifierParser] to convert + * AMQP type names into [TypeIdentifier]s. This is used as a lookup for resolving notations that are referred to by + * type name from other notations, e.g. the types of properties. + * + * We also build a [Map] of [TypeNotation] by [TypeDescriptor], which we then convert into [RemoteTypeInformation] + * while merging with the cache. + */ + fun interpret(serializationSchemas: SerializationSchemas): Map { + val (schema, transforms) = serializationSchemas + val notationLookup = schema.types.associateBy { it.name.typeIdentifier } + val byTypeDescriptor = schema.types.associateBy { it.typeDescriptor } + val enumTransformsLookup = transforms.types.asSequence().map { (name, transformSet) -> + name.typeIdentifier to interpretTransformSet(transformSet) + }.toMap() + + val interpretationState = InterpretationState(notationLookup, enumTransformsLookup, cache, emptySet()) + + return byTypeDescriptor.mapValues { (typeDescriptor, typeNotation) -> + cache.getOrPut(typeDescriptor) { interpretationState.run { typeNotation.name.typeIdentifier.interpretIdentifier() } } + } + } + + data class InterpretationState(val notationLookup: Map, + val enumTransformsLookup: Map, + val cache: MutableMap, + val seen: Set) { + + private inline fun forgetSeen(block: InterpretationState.() -> T): T = + withSeen(emptySet(), block) + + private inline fun withSeen(typeIdentifier: TypeIdentifier, block: InterpretationState.() -> T): T = + withSeen(seen + typeIdentifier, block) + + private inline fun withSeen(seen: Set, block: InterpretationState.() -> T): T = + copy(seen = seen).run(block) + + /** + * Follow a [TypeIdentifier] to the [TypeNotation] associated with it in the lookup, and interpret that notation. + * If there is no such notation, interpret the [TypeIdentifier] directly into [RemoteTypeInformation]. + * + * If we have visited this [TypeIdentifier] before while traversing the graph of related [TypeNotation]s, then we + * know we have hit a cycle and respond accordingly. + */ + fun TypeIdentifier.interpretIdentifier(): RemoteTypeInformation = + if (this in seen) RemoteTypeInformation.Cycle(this) { forgetSeen { interpretIdentifier() } } + else withSeen(this) { + val identifier = this@interpretIdentifier + notationLookup[identifier]?.interpretNotation(identifier) ?: interpretNoNotation() + } + + /** + * Either fetch from the cache, or interpret, cache, and return, the [RemoteTypeInformation] corresponding to this + * [TypeNotation]. + */ + private fun TypeNotation.interpretNotation(identifier: TypeIdentifier): RemoteTypeInformation = + cache.getOrPut(typeDescriptor) { + when (this) { + is CompositeType -> interpretComposite(identifier) + is RestrictedType -> interpretRestricted(identifier) + } + } + + /** + * Interpret the properties, interfaces and type parameters in this [TypeNotation], and return suitable + * [RemoteTypeInformation]. + */ + private fun CompositeType.interpretComposite(identifier: TypeIdentifier): RemoteTypeInformation { + val properties = fields.asSequence().map { it.interpret() }.toMap() + val typeParameters = identifier.interpretTypeParameters() + val interfaceIdentifiers = provides.map { name -> name.typeIdentifier } + val isInterface = identifier in interfaceIdentifiers + val interfaces = interfaceIdentifiers.mapNotNull { interfaceIdentifier -> + if (interfaceIdentifier == identifier) null + else interfaceIdentifier.interpretIdentifier() + } + + return if (isInterface) RemoteTypeInformation.AnInterface(typeDescriptor, identifier, properties, interfaces, typeParameters) + else RemoteTypeInformation.Composable(typeDescriptor, identifier, properties, interfaces, typeParameters) + } + + /** + * Type parameters are read off from the [TypeIdentifier] we translated the AMQP type name into. + */ + private fun TypeIdentifier.interpretTypeParameters(): List = when (this) { + is TypeIdentifier.Parameterised -> parameters.map { it.interpretIdentifier() } + else -> emptyList() + } + + /** + * Interpret a [RestrictedType] into suitable [RemoteTypeInformation]. + */ + private fun RestrictedType.interpretRestricted(identifier: TypeIdentifier): RemoteTypeInformation = when (identifier) { + is TypeIdentifier.Parameterised -> + RemoteTypeInformation.Parameterised( + typeDescriptor, + identifier, + identifier.interpretTypeParameters()) + is TypeIdentifier.ArrayOf -> + RemoteTypeInformation.AnArray( + typeDescriptor, + identifier, + identifier.componentType.interpretIdentifier()) + is TypeIdentifier.Unparameterised -> + if (choices.isEmpty()) { + RemoteTypeInformation.Unparameterised( + typeDescriptor, + identifier) + } else RemoteTypeInformation.AnEnum( + typeDescriptor, + identifier, + choices.map { it.name }, + enumTransformsLookup[identifier] ?: EnumTransforms.empty) + else -> throw NotSerializableException("Cannot interpret restricted type $this") + } + + /** + * Interpret a [Field] into a name/[RemotePropertyInformation] pair. + */ + private fun Field.interpret(): Pair { + val identifier = type.typeIdentifier + + // A type of "*" is replaced with the value of the "requires" field + val fieldTypeIdentifier = if (identifier == TypeIdentifier.TopType && !requires.isEmpty()) { + requires[0].typeIdentifier + } else identifier + + // We convert Java Object types to Java primitive types if the field is mandatory. + val fieldType = fieldTypeIdentifier.forcePrimitive(mandatory).interpretIdentifier() + + return name to RemotePropertyInformation( + fieldType, + mandatory) + } + + /** + * If there is no [TypeNotation] in the [Schema] matching a given [TypeIdentifier], we interpret the [TypeIdentifier] + * directly. + */ + private fun TypeIdentifier.interpretNoNotation(): RemoteTypeInformation = + when (this) { + is TypeIdentifier.TopType -> RemoteTypeInformation.Top + is TypeIdentifier.UnknownType -> RemoteTypeInformation.Unknown + is TypeIdentifier.ArrayOf -> + RemoteTypeInformation.AnArray( + name, + this, + componentType.interpretIdentifier()) + is TypeIdentifier.Parameterised -> + RemoteTypeInformation.Parameterised( + name, + this, + parameters.map { it.interpretIdentifier() }) + else -> RemoteTypeInformation.Unparameterised(name, this) + } + } +} + +private fun interpretTransformSet(transformSet: EnumMap>): EnumTransforms { + val defaultTransforms = transformSet[TransformTypes.EnumDefault]?.toList() ?: emptyList() + val defaults = defaultTransforms.associate { transform -> (transform as EnumDefaultSchemaTransform).new to transform.old } + val renameTransforms = transformSet[TransformTypes.Rename]?.toList() ?: emptyList() + val renames = renameTransforms.associate { transform -> (transform as RenameSchemaTransform).to to transform.from } + + return EnumTransforms(defaults, renames) +} + +private val TypeNotation.typeDescriptor: String get() = descriptor.name?.toString() ?: + throw NotSerializableException("Type notation has no type descriptor: $this") + +private val String.typeIdentifier get(): TypeIdentifier = AMQPTypeIdentifierParser.parse(this) + +/** + * Force e.g. [java.lang.Integer] to `int`, if it is the type of a mandatory field. + */ +private fun TypeIdentifier.forcePrimitive(mandatory: Boolean) = + if (mandatory) primitives[this] ?: this + else this + +private val primitives = sequenceOf( + Boolean::class, + Byte::class, + Char::class, + Int::class, + Short::class, + Long::class, + Float::class, + Double::class).associate { + TypeIdentifier.forClass(it.javaObjectType) to TypeIdentifier.forClass(it.javaPrimitiveType!!) +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt index 5be95c4306..1f530f5434 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt @@ -172,7 +172,7 @@ abstract class AbstractAMQPSerializationScheme( // Not used as a simple direct import to facilitate testing open val publicKeySerializer: CustomSerializer<*> = net.corda.serialization.internal.amqp.custom.PublicKeySerializer - private fun getSerializerFactory(context: SerializationContext): SerializerFactory { + fun getSerializerFactory(context: SerializationContext): SerializerFactory { val key = Pair(context.whitelist, context.deserializationClassLoader) // ConcurrentHashMap.get() is lock free, but computeIfAbsent is not, even if the key is in the map already. return serializerFactoriesForContexts[key] ?: serializerFactoriesForContexts.computeIfAbsent(key) { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParser.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParser.kt new file mode 100644 index 0000000000..4309b03bbe --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParser.kt @@ -0,0 +1,190 @@ +package net.corda.serialization.internal.amqp + +import com.google.common.primitives.Primitives +import net.corda.serialization.internal.model.TypeIdentifier +import org.apache.qpid.proton.amqp.* +import java.io.NotSerializableException +import java.lang.StringBuilder +import java.util.* + +/** + * Thrown if the type string parser enters an illegal state. + */ +class IllegalTypeNameParserStateException(message: String): NotSerializableException(message) + +/** + * Provides a state machine which knows how to parse AMQP type strings into [TypeIdentifier]s. + */ +object AMQPTypeIdentifierParser { + + internal const val MAX_TYPE_PARAM_DEPTH = 32 + private const val MAX_ARRAY_DEPTH = 32 + + /** + * Given a string representing a serialized AMQP type, construct a TypeIdentifier for that string. + * + * @param typeString The AMQP type string to parse + * @return A [TypeIdentifier] representing the type represented by the input string. + */ + fun parse(typeString: String): TypeIdentifier { + validate(typeString) + return typeString.fold(ParseState.ParsingRawType(null)) { state, c -> + state.accept(c) + }.getTypeIdentifier() + } + + // Make sure our inputs aren't designed to blow things up. + private fun validate(typeString: String) { + var maxTypeParamDepth = 0 + var typeParamdepth = 0 + + var maxArrayDepth = 0 + var wasArray = false + var arrayDepth = 0 + + for (c in typeString) { + if (c.isWhitespace() || c.isJavaIdentifierPart() || c.isJavaIdentifierStart() || + c == '.' || c == ',' || c == '?' || c == '*') continue + + when(c) { + '<' -> maxTypeParamDepth = Math.max(++typeParamdepth, typeParamdepth) + '>' -> typeParamdepth-- + '[' -> { + arrayDepth = if (wasArray) arrayDepth + 2 else 1 + maxArrayDepth = Math.max(maxArrayDepth,arrayDepth) + } + ']' -> arrayDepth-- + else -> throw IllegalTypeNameParserStateException("Type name '$typeString' contains illegal character '$c'") + } + wasArray = c == ']' + } + if (maxTypeParamDepth >= MAX_TYPE_PARAM_DEPTH) + throw IllegalTypeNameParserStateException("Nested depth of type parameters exceeds maximum of $MAX_TYPE_PARAM_DEPTH") + + if (maxArrayDepth >= MAX_ARRAY_DEPTH) + throw IllegalTypeNameParserStateException("Nested depth of arrays exceeds maximum of $MAX_ARRAY_DEPTH") + } + + private sealed class ParseState { + abstract val parent: ParseState.ParsingParameterList? + abstract fun accept(c: Char): ParseState + abstract fun getTypeIdentifier(): TypeIdentifier + + fun unexpected(c: Char): ParseState = throw IllegalTypeNameParserStateException("Unexpected character: '$c'") + fun notInParameterList(c: Char): ParseState = + throw IllegalTypeNameParserStateException("'$c' encountered, but not parsing type parameter list") + + /** + * We are parsing a raw type name, either at the top level or as part of a list of type parameters. + */ + data class ParsingRawType(override val parent: ParseState.ParsingParameterList?, val buffer: StringBuilder = StringBuilder()) : ParseState() { + override fun accept(c: Char) = when (c) { + ',' -> + if (parent == null) notInParameterList(c) + else ParsingRawType(parent.addParameter(getTypeIdentifier())) + '[' -> ParsingArray(getTypeIdentifier(), parent) + ']' -> unexpected(c) + '<' -> ParsingRawType(ParsingParameterList(getTypeName(), parent)) + '>' -> parent?.addParameter(getTypeIdentifier())?.accept(c) ?: notInParameterList(c) + else -> apply { buffer.append(c) } + } + + private fun getTypeName(): String { + val typeName = buffer.toString().trim() + if (typeName.contains(' ')) + throw IllegalTypeNameParserStateException("Illegal whitespace in type name $typeName") + return typeName + } + + override fun getTypeIdentifier(): TypeIdentifier { + val typeName = getTypeName() + return when (typeName) { + "*" -> TypeIdentifier.TopType + "?" -> TypeIdentifier.UnknownType + in simplified -> simplified[typeName]!! + else -> TypeIdentifier.Unparameterised(typeName) + } + } + } + + /** + * We are parsing a parameter list, and expect either to start a new parameter, add array-ness to the last + * parameter we have, or end the list. + */ + data class ParsingParameterList(val typeName: String, override val parent: ParsingParameterList?, val parameters: List = emptyList()) : ParseState() { + override fun accept(c: Char) = when (c) { + ' ' -> this + ',' -> ParsingRawType(this) + '[' -> + if (parameters.isEmpty()) unexpected(c) + else ParsingArray( + // Start adding array-ness to the last parameter we have. + parameters[parameters.lastIndex], + // Take a copy of this state, dropping the last parameter which will be added back on + // when array parsing completes. + copy(parameters = parameters.subList(0, parameters.lastIndex))) + '>' -> parent?.addParameter(getTypeIdentifier()) ?: Complete(getTypeIdentifier()) + else -> unexpected(c) + } + + fun addParameter(parameter: TypeIdentifier) = copy(parameters = parameters + parameter) + + override fun getTypeIdentifier() = TypeIdentifier.Parameterised(typeName, null, parameters) + } + + /** + * We are adding array-ness to some type identifier. + */ + data class ParsingArray(val componentType: TypeIdentifier, override val parent: ParseState.ParsingParameterList?) : ParseState() { + override fun accept(c: Char) = when (c) { + ' ' -> this + 'p' -> ParsingArray(forcePrimitive(componentType), parent) + ']' -> parent?.addParameter(getTypeIdentifier()) ?: Complete(getTypeIdentifier()) + else -> unexpected(c) + } + + override fun getTypeIdentifier() = TypeIdentifier.ArrayOf(componentType) + + private fun forcePrimitive(componentType: TypeIdentifier): TypeIdentifier = + TypeIdentifier.forClass(Primitives.unwrap(componentType.getLocalType().asClass())) + } + + /** + * We have a complete type identifier, and all we can do to it is add array-ness. + */ + data class Complete(val identifier: TypeIdentifier) : ParseState() { + override val parent: ParseState.ParsingParameterList? get() = null + override fun accept(c: Char): ParseState = when (c) { + ' ' -> this + '[' -> ParsingArray(identifier, null) + else -> unexpected(c) + } + + override fun getTypeIdentifier() = identifier + } + } + + private val simplified = mapOf( + "string" to String::class, + "boolean" to Boolean::class, + "byte" to Byte::class, + "char" to Char::class, + "int" to Int::class, + "short" to Short::class, + "long" to Long::class, + "double" to Double::class, + "float" to Float::class, + "ubyte" to UnsignedByte::class, + "uint" to UnsignedInteger::class, + "ushort" to UnsignedShort::class, + "ulong" to UnsignedLong::class, + "decimal32" to Decimal32::class, + "decimal64" to Decimal64::class, + "decimal128" to Decimal128::class, + "binary" to ByteArray::class, + "timestamp" to Date::class, + "uuid" to UUID::class, + "symbol" to Symbol::class).mapValues { (_, v) -> + TypeIdentifier.forClass(v.javaObjectType) + } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifiers.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifiers.kt new file mode 100644 index 0000000000..9b227843b3 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifiers.kt @@ -0,0 +1,63 @@ +package net.corda.serialization.internal.amqp + +import net.corda.serialization.internal.model.TypeIdentifier +import org.apache.qpid.proton.amqp.* +import java.lang.reflect.Type +import java.util.* + +object AMQPTypeIdentifiers { + fun isPrimitive(type: Type): Boolean = isPrimitive(TypeIdentifier.forGenericType(type)) + fun isPrimitive(typeIdentifier: TypeIdentifier) = typeIdentifier in primitiveTypeNamesByName + + fun primitiveTypeName(type: Type): String? = + primitiveTypeNamesByName[TypeIdentifier.forGenericType(type)] + + private val primitiveTypeNamesByName = sequenceOf( + Character::class to "char", + Char::class to "char", + Boolean::class to "boolean", + Byte::class to "byte", + UnsignedByte::class to "ubyte", + Short::class to "short", + UnsignedShort::class to "ushort", + Int::class to "int", + UnsignedInteger::class to "uint", + Long::class to "long", + UnsignedLong::class to "ulong", + Float::class to "float", + Double::class to "double", + Decimal32::class to "decimal32", + Decimal64::class to "decimal62", + Decimal128::class to "decimal128", + Date::class to "timestamp", + UUID::class to "uuid", + ByteArray::class to "binary", + String::class to "string", + Symbol::class to "symbol") + .flatMap { (klass, name) -> + val typeIdentifier = TypeIdentifier.forClass(klass.javaObjectType) + val primitiveTypeIdentifier = klass.javaPrimitiveType?.let { TypeIdentifier.forClass(it) } + if (primitiveTypeIdentifier == null) sequenceOf(typeIdentifier to name) + else sequenceOf(typeIdentifier to name, primitiveTypeIdentifier to name) + }.toMap() + + fun nameForType(typeIdentifier: TypeIdentifier): String = when(typeIdentifier) { + is TypeIdentifier.Erased -> typeIdentifier.name + is TypeIdentifier.Unparameterised -> primitiveTypeNamesByName[typeIdentifier] ?: typeIdentifier.name + is TypeIdentifier.UnknownType, + is TypeIdentifier.TopType -> "?" + is TypeIdentifier.ArrayOf -> + if (typeIdentifier == primitiveByteArrayType) "binary" + else nameForType(typeIdentifier.componentType) + + if (typeIdentifier.componentType is TypeIdentifier.Unparameterised && + typeIdentifier.componentType.isPrimitive) "[p]" + else "[]" + is TypeIdentifier.Parameterised -> typeIdentifier.name + typeIdentifier.parameters.joinToString(", ", "<", ">") { + nameForType(it) + } + } + + private val primitiveByteArrayType = TypeIdentifier.ArrayOf(TypeIdentifier.forClass(Byte::class.javaPrimitiveType!!)) + + fun nameForType(type: Type): String = nameForType(TypeIdentifier.forGenericType(type)) +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt new file mode 100644 index 0000000000..61478dc43c --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializerRegistry.kt @@ -0,0 +1,94 @@ +package net.corda.serialization.internal.amqp + +import net.corda.core.internal.uncheckedCast +import net.corda.core.utilities.contextLogger +import net.corda.serialization.internal.model.DefaultCacheProvider +import net.corda.serialization.internal.model.TypeIdentifier +import java.lang.reflect.Type + +interface CustomSerializerRegistry { + /** + * Register a custom serializer for any type that cannot be serialized or deserialized by the default serializer + * that expects to find getters and a constructor with a parameter for each property. + */ + fun register(customSerializer: CustomSerializer) + fun registerExternal(customSerializer: CorDappCustomSerializer) + + fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer? +} + +class CachingCustomSerializerRegistry( + private val descriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry) + : CustomSerializerRegistry { + + companion object { + val logger = contextLogger() + } + + private data class CustomSerializerIdentifier(val actualTypeIdentifier: TypeIdentifier, val declaredTypeIdentifier: TypeIdentifier) + + private val customSerializersCache: MutableMap> = DefaultCacheProvider.createCache() + private var customSerializers: List = emptyList() + + /** + * Register a custom serializer for any type that cannot be serialized or deserialized by the default serializer + * that expects to find getters and a constructor with a parameter for each property. + */ + override fun register(customSerializer: CustomSerializer) { + logger.trace("action=\"Registering custom serializer\", class=\"${customSerializer.type}\"") + + descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) { + customSerializers += customSerializer + for (additional in customSerializer.additionalSerializers) { + register(additional) + } + customSerializer + } + } + + override fun registerExternal(customSerializer: CorDappCustomSerializer) { + logger.trace("action=\"Registering external serializer\", class=\"${customSerializer.type}\"") + + descriptorBasedSerializerRegistry.getOrBuild(customSerializer.typeDescriptor.toString()) { + customSerializers += customSerializer + customSerializer + } + } + + override fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer? { + val typeIdentifier = CustomSerializerIdentifier( + TypeIdentifier.forClass(clazz), + TypeIdentifier.forGenericType(declaredType)) + + return customSerializersCache[typeIdentifier] + ?: doFindCustomSerializer(clazz, declaredType)?.also { serializer -> + customSerializersCache.putIfAbsent(typeIdentifier, serializer) + } + } + + private fun doFindCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer? { + // e.g. Imagine if we provided a Map serializer this way, then it won't work if the declared type is + // AbstractMap, only Map. Otherwise it needs to inject additional schema for a RestrictedType source of the + // super type. Could be done, but do we need it? + for (customSerializer in customSerializers) { + if (customSerializer.isSerializerFor(clazz)) { + val declaredSuperClass = declaredType.asClass().superclass + + return if (declaredSuperClass == null + || !customSerializer.isSerializerFor(declaredSuperClass) + || !customSerializer.revealSubclassesInSchema + ) { + logger.debug("action=\"Using custom serializer\", class=${clazz.typeName}, " + + "declaredType=${declaredType.typeName}") + + @Suppress("UNCHECKED_CAST") + customSerializer as? AMQPSerializer + } else { + // Make a subclass serializer for the subclass and return that... + CustomSerializer.SubClass(clazz, uncheckedCast(customSerializer)) + } + } + } + return null + } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt new file mode 100644 index 0000000000..2a2d17127d --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DescriptorBasedSerializerRegistry.kt @@ -0,0 +1,29 @@ +package net.corda.serialization.internal.amqp + +import net.corda.serialization.internal.model.DefaultCacheProvider + +/** + * The quickest way to find a serializer, if one has already been generated, is to look it up by type descriptor. + * + * This registry gets shared around between various participants that might want to use it as a lookup, or register + * serialisers that they have created with it. + */ +interface DescriptorBasedSerializerRegistry { + operator fun get(descriptor: String): AMQPSerializer? + operator fun set(descriptor: String, serializer: AMQPSerializer) + fun getOrBuild(descriptor: String, builder: () -> AMQPSerializer): AMQPSerializer +} + +class DefaultDescriptorBasedSerializerRegistry: DescriptorBasedSerializerRegistry { + + private val registry: MutableMap> = DefaultCacheProvider.createCache() + + override fun get(descriptor: String): AMQPSerializer? = registry[descriptor] + + override fun set(descriptor: String, serializer: AMQPSerializer) { + registry.putIfAbsent(descriptor, serializer) + } + + override fun getOrBuild(descriptor: String, builder: () -> AMQPSerializer) = + get(descriptor) ?: builder().also { newSerializer -> this[descriptor] = newSerializer } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializers.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializers.kt index 65a8998a14..517af0406a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializers.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializers.kt @@ -52,6 +52,8 @@ class PublicPropertyReader(private val readMethod: Method) : PropertyReader() { } override fun isNullable(): Boolean = readMethod.returnsNullable() + + val genericReturnType get() = readMethod.genericReturnType } /** diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt index 1cfa23f1eb..8e2e1e1330 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt @@ -118,7 +118,7 @@ interface SerializerFactory { Float::class.java to "float", Double::class.java to "double", Decimal32::class.java to "decimal32", - Decimal64::class.java to "decimal62", + Decimal64::class.java to "decimal64", Decimal128::class.java to "decimal128", Date::class.java to "timestamp", UUID::class.java to "uuid", diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/WhitelistBasedTypeModelConfiguration.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/WhitelistBasedTypeModelConfiguration.kt new file mode 100644 index 0000000000..dfb7651b54 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/WhitelistBasedTypeModelConfiguration.kt @@ -0,0 +1,45 @@ +package net.corda.serialization.internal.amqp + +import com.google.common.primitives.Primitives +import net.corda.core.serialization.ClassWhitelist +import net.corda.serialization.internal.model.LocalTypeModelConfiguration +import org.apache.qpid.proton.amqp.* +import java.lang.reflect.Type +import java.util.* + +/** + * [LocalTypeModelConfiguration] based on a [ClassWhitelist] + */ +class WhitelistBasedTypeModelConfiguration( + private val whitelist: ClassWhitelist, + private val customSerializerRegistry: CustomSerializerRegistry) + : LocalTypeModelConfiguration { + override fun isExcluded(type: Type): Boolean = whitelist.isNotWhitelisted(type.asClass()) + override fun isOpaque(type: Type): Boolean = Primitives.unwrap(type.asClass()) in opaqueTypes || + customSerializerRegistry.findCustomSerializer(type.asClass(), type) != null +} + +// Copied from SerializerFactory so that we can have equivalent behaviour, for now. +private val opaqueTypes = setOf( + Character::class.java, + Char::class.java, + Boolean::class.java, + Byte::class.java, + UnsignedByte::class.java, + Short::class.java, + UnsignedShort::class.java, + Int::class.java, + UnsignedInteger::class.java, + Long::class.java, + UnsignedLong::class.java, + Float::class.java, + Double::class.java, + Decimal32::class.java, + Decimal64::class.java, + Decimal128::class.java, + Date::class.java, + UUID::class.java, + ByteArray::class.java, + String::class.java, + Symbol::class.java +) \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt index f1a422f22e..fb05ce1695 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt @@ -148,7 +148,9 @@ class ClassCarpenterImpl @JvmOverloads constructor (override val whitelist: Clas } } - require(schema.name in _loaded) + if (schema.name !in _loaded){ + throw ClassNotFoundException(schema.name) + } return _loaded[schema.name]!! } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/MetaCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/MetaCarpenter.kt index b5e6593286..445c3ce7da 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/MetaCarpenter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/MetaCarpenter.kt @@ -83,6 +83,7 @@ abstract class MetaCarpenterBase(val schemas: CarpenterMetaSchema, val cc: Class // carpented class existing and remove it from their dependency list, If that // list is now empty we have no impediment to carpenting that class up schemas.dependsOn.remove(newObject.name)?.forEach { dependent -> + require(newObject.name in schemas.dependencies[dependent]!!.second) schemas.dependencies[dependent]?.second?.remove(newObject.name) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt index ba40dc20f8..8e639ce810 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt @@ -72,8 +72,7 @@ open class NonNullableField(field: Class) : ClassField(field) { } override fun nullTest(mv: MethodVisitor, slot: Int) { - require(name != unsetName) - + check(name != unsetName) {"Property this.name cannot be $unsetName"} if (!field.isPrimitive) { with(mv) { visitVarInsn(ALOAD, 0) // load this @@ -109,7 +108,7 @@ class NullableField(field: Class) : ClassField(field) { } override fun nullTest(mv: MethodVisitor, slot: Int) { - require(name != unsetName) + require(name != unsetName){"Property this.name cannot be $unsetName"} } } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/CarpentryDependencyGraph.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/CarpentryDependencyGraph.kt new file mode 100644 index 0000000000..6b6edfac88 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/CarpentryDependencyGraph.kt @@ -0,0 +1,124 @@ +package net.corda.serialization.internal.model + +import java.io.NotSerializableException +import java.lang.reflect.Type + +/** + * Once we have the complete graph of types requiring carpentry to hand, we can use it to sort those types in reverse- + * dependency order, i.e. beginning with those types that have no dependencies on other types, then the types that + * depended on those types, and so on. This means we can feed types directly to the [RemoteTypeCarpenter], and don't + * have to use the [CarpenterMetaSchema]. + * + * @param typesRequiringCarpentry The set of [RemoteTypeInformation] for types that are not reachable by the current + * classloader. + */ +class CarpentryDependencyGraph private constructor(private val typesRequiringCarpentry: Set) { + + companion object { + /** + * Sort the [typesRequiringCarpentry] into reverse-dependency order, then pass them to the provided + * [Type]-builder, collating the results into a [Map] of [Type] by [TypeIdentifier] + */ + fun buildInReverseDependencyOrder( + typesRequiringCarpentry: Set, + getOrBuild: (RemoteTypeInformation) -> Type): Map = + CarpentryDependencyGraph(typesRequiringCarpentry).buildInOrder(getOrBuild) + } + + /** + * A map of inbound edges by node. + * + * A [RemoteTypeInformation] map key is a type that requires other types to have been constructed before it can be + * constructed. + * + * Each [RemoteTypeInformation] in the corresponding [Set] map value is one of the types that the key-type depends on. + * + * No key ever maps to an empty set: types with no dependencies are not included in this map. + */ + private val dependencies = mutableMapOf>() + + /** + * If it is in [typesRequiringCarpentry], then add an edge from [dependee] to this type to the [dependencies] graph. + */ + private fun RemoteTypeInformation.dependsOn(dependee: RemoteTypeInformation) = dependsOn(listOf(dependee)) + + /** + * Add an edge from each of these [dependees] that are in [typesRequiringCarpentry] to this type to the + * [dependencies] graph. + */ + private fun RemoteTypeInformation.dependsOn(dependees: Collection) { + val dependeesInTypesRequiringCarpentry = dependees.filter { it in typesRequiringCarpentry } + if (dependeesInTypesRequiringCarpentry.isEmpty()) return // we don't want to put empty sets into the map. + dependencies.compute(this) { _, dependees -> + dependees?.apply { addAll(dependeesInTypesRequiringCarpentry) } ?: + dependeesInTypesRequiringCarpentry.toMutableSet() + } + } + + /** + * Traverses each of the [typesRequiringCarpentry], building (or obtaining from a cache) the corresponding [Type] + * and populating them into a [Map] of [Type] by [TypeIdentifier]. + */ + private fun buildInOrder(getOrBuild: (RemoteTypeInformation) -> Type): Map { + typesRequiringCarpentry.forEach { it.recordDependencies() } + + return topologicalSort(typesRequiringCarpentry).associate { information -> + information.typeIdentifier to getOrBuild(information) + } + } + + /** + * Record appropriate dependencies for each type of [RemoteTypeInformation] + */ + private fun RemoteTypeInformation.recordDependencies() = when (this) { + is RemoteTypeInformation.Composable -> { + dependsOn(typeParameters) + dependsOn(interfaces) + dependsOn(properties.values.map { it.type }) + } + is RemoteTypeInformation.AnInterface -> { + dependsOn(typeParameters) + dependsOn(interfaces) + dependsOn(properties.values.map { it.type }) + } + is RemoteTypeInformation.AnArray -> dependsOn(componentType) + is RemoteTypeInformation.Parameterised -> dependsOn(typeParameters) + else -> {} + } + + /** + * Separate out those [types] which have [noDependencies] from those which still have dependencies. + * + * Remove the types with no dependencies from the graph, identifying which types are left with no inbound dependees + * as a result, then return the types with no dependencies concatenated with the [topologicalSort] of the remaining + * types, minus the newly-independent types. + */ + private fun topologicalSort( + types: Set, + noDependencies: Set = types - dependencies.keys): Sequence { + // Types which still have dependencies. + val remaining = dependencies.keys.toSet() + + // Remove the types which have no dependencies from the dependencies of the remaining types, and identify + // those types which have no dependencies left after we've done this. + val newlyIndependent = dependencies.asSequence().mapNotNull { (dependent, dependees) -> + dependees.removeAll(noDependencies) + if (dependees.isEmpty()) dependent else null + }.toSet() + + // If there are still types with dependencies, and we have no dependencies we can remove, then we can't continue. + if (newlyIndependent.isEmpty() && dependencies.isNotEmpty()) { + throw NotSerializableException( + "Cannot build dependencies for " + + dependencies.keys.map { it.typeIdentifier.prettyPrint(false) }) + } + + // Remove the types which have no dependencies remaining, maintaining the invariant that no key maps to an + // empty set. + dependencies.keys.removeAll(newlyIndependent) + + // Return the types that had no dependencies, then recurse to process the remainder. + return noDependencies.asSequence() + + if (dependencies.isEmpty()) newlyIndependent.asSequence() else topologicalSort(remaining, newlyIndependent) + } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalPropertyInformation.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalPropertyInformation.kt new file mode 100644 index 0000000000..69b223d32b --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalPropertyInformation.kt @@ -0,0 +1,71 @@ +package net.corda.serialization.internal.model + +import java.lang.reflect.Field +import java.lang.reflect.Method + +/** + * Represents the information we have about a property of a type. + */ +sealed class LocalPropertyInformation(val isCalculated: Boolean) { + + /** + * [LocalTypeInformation] for the type of the property. + */ + abstract val type: LocalTypeInformation + + /** + * True if the property is a primitive type or is flagged as non-nullable, false otherwise. + */ + abstract val isMandatory: Boolean + + /** + * A property of an interface, for which we have only a getter method. + * + * @param observedGetter The method which can be used to obtain the value of this property from an instance of its owning type. + */ + data class ReadOnlyProperty(val observedGetter: Method, override val type: LocalTypeInformation, override val isMandatory: Boolean) : LocalPropertyInformation(false) + + /** + * A property for which we have both a getter, and a matching slot in an array of constructor parameters. + * + * @param observedGetter The method which can be used to obtain the value of this property from an instance of its owning type. + * @param constructorSlot The [ConstructorSlot] to which the property corresponds, used to populate an array of + * constructor arguments when creating instances of its owning type. + */ + data class ConstructorPairedProperty(val observedGetter: Method, val constructorSlot: ConstructorSlot, override val type: LocalTypeInformation, override val isMandatory: Boolean) : LocalPropertyInformation(false) + + /** + * A property for which we have no getter, but for which there is a backing field a matching slot in an array of + * constructor parameters. + * + * @param observedField The field which can be used to obtain the value of this property from an instance of its owning type. + * @param constructorSlot The [ConstructorSlot] to which the property corresponds, used to populate an array of + * constructor arguments when creating instances of its owning type. + */ + data class PrivateConstructorPairedProperty(val observedField: Field, val constructorSlot: ConstructorSlot, override val type: LocalTypeInformation, override val isMandatory: Boolean) : LocalPropertyInformation(false) + + /** + * A property for which we have both getter and setter methods (usually belonging to a POJO which is initialised + * with the default no-argument constructor and then configured via setters). + * + * @param observedGetter The method which can be used to obtain the value of this property from an instance of its owning type. + * @param observedSetter The method which can be used to set the value of this property on an instance of its owning type. + */ + data class GetterSetterProperty(val observedGetter: Method, val observedSetter: Method, override val type: LocalTypeInformation, override val isMandatory: Boolean) : LocalPropertyInformation(false) + + /** + * A property for which we have only a getter method, which is annotated with [SerializableCalculatedProperty]. + */ + data class CalculatedProperty(val observedGetter: Method, override val type: LocalTypeInformation, override val isMandatory: Boolean) : LocalPropertyInformation(true) +} + + +/** + * References a slot in an array of constructor parameters. + */ +data class ConstructorSlot(val parameterIndex: Int, val constructorInformation: LocalConstructorInformation) { + val parameterInformation get() = constructorInformation.parameters.getOrNull(parameterIndex) ?: + throw IllegalStateException("Constructor slot refers to parameter #$parameterIndex " + + "of constructor $constructorInformation, " + + "but constructor has only ${constructorInformation.parameters.size} parameters") +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformation.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformation.kt new file mode 100644 index 0000000000..206417138b --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformation.kt @@ -0,0 +1,374 @@ +package net.corda.serialization.internal.model + +import java.lang.reflect.* +import kotlin.reflect.KFunction +import java.util.* + +typealias PropertyName = String + +/** + * The [LocalTypeInformation] captured for a [Type] gathers together everything that can be ascertained about the type + * through runtime reflection, in the form of a directed acyclic graph (DAG) of types and relationships between types. + * + * Types can be related in the following ways: + * + * * Type A is the type of a _property_ of type B. + * * Type A is the type of an _interface_ of type B. + * * Type A is the type of the _superclass_ of type B. + * * Type A is the type of a _type parameter_ of type B. + * * Type A is an _array type_, of which type B is the _component type_. + * + * All of these relationships are represented by references and collections held by the objects representing the nodes + * themselves. + * + * A type is [Composable] if it is isomorphic to a dictionary of its property values, i.e. if we can obtain an instance + * of the type from a dictionary containing typed key/value pairs corresponding to its properties, and a dictionary from + * an instance of the type, and can round-trip (in both directions) between these representations without losing + * information. This is the basis for compositional serialization, i.e. building a serializer for a type out of the + * serializers we have for its property types. + * + * A type is [Atomic] if it cannot be decomposed or recomposed in this fashion (usually because it is the type of a + * scalar value of some sort, such as [Int]), and [Opaque] if we have chosen not to investigate its composability, + * typically because it is handled by a custom serializer. + * + * Abstract types are represented by [AnInterface] and [Abstract], the difference between them being that an [Abstract] + * type may have a superclass. + * + * If a concrete type does not have a unique deserialization constructor, it is represented by [NonComposable], meaning + * that we know how to take it apart but do not know how to put it back together again. + * + * An array of any type is represented by [ArrayOf]. Enums are represented by [AnEnum]. + * + * The type of [Any]/[java.lang.Object] is represented by [Top]. Unbounded wildcards, or wildcards whose upper bound is + * [Top], are represented by [Unknown]. Bounded wildcards are always resolved to their upper bounds, e.g. + * `List` becomes `List`. + * + * If we encounter a cycle while traversing the DAG, the type on which traversal detected the cycle is represented by + * [Cycle], and no further traversal is attempted from that type. Kotlin objects are represented by [Singleton]. + */ +sealed class LocalTypeInformation { + + companion object { + /** + * Using the provided [LocalTypeLookup] to record and locate already-visited nodes, traverse the DAG of related + * types beginning the with provided [Type] and construct a complete set of [LocalTypeInformation] for that type. + * + * @param type The [Type] to obtain [LocalTypeInformation] for. + * @param lookup The [LocalTypeLookup] to use to find previously-constructed [LocalTypeInformation]. + */ + fun forType(type: Type, lookup: LocalTypeLookup): LocalTypeInformation = + LocalTypeInformationBuilder(lookup).build(type, TypeIdentifier.forGenericType(type)) + } + + /** + * The actual type which was observed when constructing this type information. + */ + abstract val observedType: Type + + /** + * The [TypeIdentifier] for the type represented by this type information, used to cross-reference with + * [RemoteTypeInformation]. + */ + abstract val typeIdentifier: TypeIdentifier + + /** + * Obtain a multi-line, recursively-indented representation of this type information. + * + * @param simplifyClassNames By default, class names are printed as their "simple" class names, i.e. "String" instead + * of "java.lang.String". If this is set to `false`, then the full class name will be printed instead. + */ + fun prettyPrint(simplifyClassNames: Boolean = true): String = + LocalTypeInformationPrettyPrinter(simplifyClassNames).prettyPrint(this) + + /** + * The [LocalTypeInformation] corresponding to an unbounded wildcard ([TypeIdentifier.UnknownType]) + */ + object Unknown : LocalTypeInformation() { + override val observedType get() = TypeIdentifier.UnknownType.getLocalType() + override val typeIdentifier get() = TypeIdentifier.UnknownType + } + + /** + * The [LocalTypeInformation] corresponding to [java.lang.Object] / [Any] ([TypeIdentifier.TopType]) + */ + object Top : LocalTypeInformation() { + override val observedType get() = TypeIdentifier.TopType.getLocalType() + override val typeIdentifier get() = TypeIdentifier.TopType + } + + /** + * The [LocalTypeInformation] emitted if we hit a cycle while traversing the graph of related types. + */ + data class Cycle( + override val observedType: Type, + override val typeIdentifier: TypeIdentifier, + private val _follow: () -> LocalTypeInformation) : LocalTypeInformation() { + val follow: LocalTypeInformation get() = _follow() + + // Custom equals / hashcode because otherwise the "follow" lambda makes equality harder to reason about. + override fun equals(other: Any?): Boolean = + other is Cycle && + other.observedType == observedType && + other.typeIdentifier == typeIdentifier + + override fun hashCode(): Int = Objects.hash(observedType, typeIdentifier) + + override fun toString(): String = "Cycle($observedType, $typeIdentifier)" + } + + /** + * May in fact be a more complex class, but is treated as if atomic, i.e. we don't further expand its properties. + */ + data class Opaque(override val observedType: Class<*>, override val typeIdentifier: TypeIdentifier, + private val _expand: () -> LocalTypeInformation) : LocalTypeInformation() { + val expand: LocalTypeInformation get() = _expand() + + // Custom equals / hashcode because otherwise the "expand" lambda makes equality harder to reason about. + override fun equals(other: Any?): Boolean = + other is Cycle && + other.observedType == observedType && + other.typeIdentifier == typeIdentifier + + override fun hashCode(): Int = Objects.hash(observedType, typeIdentifier) + + override fun toString(): String = "Opaque($observedType, $typeIdentifier)" + } + + /** + * Represents a scalar type such as [Int]. + */ + data class Atomic(override val observedType: Class<*>, override val typeIdentifier: TypeIdentifier) : LocalTypeInformation() + + /** + * Represents an array of some other type. + * + * @param componentType The [LocalTypeInformation] for the component type of the array (e.g. [Int], if the type is [IntArray]) + */ + data class AnArray(override val observedType: Type, override val typeIdentifier: TypeIdentifier, val componentType: LocalTypeInformation) : LocalTypeInformation() + + /** + * Represents an `enum` + * + * @param members The string names of the members of the enum. + * @param superclass [LocalTypeInformation] for the superclass of the type (as enums can inherit from other types). + * @param interfaces [LocalTypeInformation] for each interface implemented by the type. + */ + data class AnEnum( + override val observedType: Class<*>, + override val typeIdentifier: TypeIdentifier, + val members: List, + val interfaces: List): LocalTypeInformation() + + /** + * Represents a type whose underlying class is an interface. + * + * @param properties [LocalPropertyInformation] for the read-only properties of the interface, i.e. its "getter" methods. + * @param interfaces [LocalTypeInformation] for the interfaces extended by this interface. + * @param typeParameters [LocalTypeInformation] for the resolved type parameters of the type. + */ + data class AnInterface( + override val observedType: Type, + override val typeIdentifier: TypeIdentifier, + val properties: Map, + val interfaces: List, + val typeParameters: List) : LocalTypeInformation() + + /** + * Represents a type whose underlying class is abstract. + * + * @param properties [LocalPropertyInformation] for the read-only properties of the interface, i.e. its "getter" methods. + * @param superclass [LocalTypeInformation] for the superclass of the underlying class of this type. + * @param interfaces [LocalTypeInformation] for the interfaces extended by this interface. + * @param typeParameters [LocalTypeInformation] for the resolved type parameters of the type. + */ + data class Abstract( + override val observedType: Type, + override val typeIdentifier: TypeIdentifier, + val properties: Map, + val superclass: LocalTypeInformation, + val interfaces: List, + val typeParameters: List) : LocalTypeInformation() + + /** + * Represents a type which has only a single instantiation, e.g. a Kotlin `object`. + * + * @param superclass [LocalTypeInformation] for the superclass of the underlying class of this type. + * @param interfaces [LocalTypeInformation] for the interfaces extended by this interface. + */ + data class Singleton(override val observedType: Type, override val typeIdentifier: TypeIdentifier, val superclass: LocalTypeInformation, val interfaces: List) : LocalTypeInformation() + + /** + * Represents a type whose instances can be reversibly decomposed into dictionaries of typed values. + * + * @param constructor [LocalConstructorInformation] for the constructor used when building instances of this type + * out of dictionaries of typed values. + * @param properties [LocalPropertyInformation] for the properties of the interface. + * @param superclass [LocalTypeInformation] for the superclass of the underlying class of this type. + * @param interfaces [LocalTypeInformation] for the interfaces extended by this interface. + * @param typeParameters [LocalTypeInformation] for the resolved type parameters of the type. + */ + data class Composable( + override val observedType: Type, + override val typeIdentifier: TypeIdentifier, + val constructor: LocalConstructorInformation, + val evolverConstructors: List, + val properties: Map, + val superclass: LocalTypeInformation, + val interfaces: List, + val typeParameters: List) : LocalTypeInformation() + + /** + * Represents a type whose instances may have observable properties (represented by "getter" methods), but for which + * we do not possess a method (such as a unique "deserialization constructor" satisfied by these properties) for + * creating a new instance from a dictionary of property values. + * + * @param constructor [LocalConstructorInformation] for the constructor of this type, if there is one. + * @param properties [LocalPropertyInformation] for the properties of the interface. + * @param superclass [LocalTypeInformation] for the superclass of the underlying class of this type. + * @param interfaces [LocalTypeInformation] for the interfaces extended by this interface. + * @param typeParameters [LocalTypeInformation] for the resolved type parameters of the type. + */ + data class NonComposable( + override val observedType: Type, + override val typeIdentifier: TypeIdentifier, + val constructor: LocalConstructorInformation?, + val properties: Map, + val superclass: LocalTypeInformation, + val interfaces: List, + val typeParameters: List) : LocalTypeInformation() + + /** + * Represents a type whose underlying class is a collection class such as [List] with a single type parameter. + * + * @param elementType [LocalTypeInformation] for the resolved type parameter of the type, i.e. the type of its + * elements. [Unknown] if the type is erased. + */ + data class ACollection(override val observedType: Type, override val typeIdentifier: TypeIdentifier, val elementType: LocalTypeInformation) : LocalTypeInformation() { + val isErased: Boolean get() = typeIdentifier is TypeIdentifier.Erased + + fun withElementType(parameter: LocalTypeInformation): ACollection = when(typeIdentifier) { + is TypeIdentifier.Erased -> { + val unerasedType = typeIdentifier.toParameterized(listOf(parameter.typeIdentifier)) + ACollection( + unerasedType.getLocalType(this::class.java.classLoader), + unerasedType, + parameter) + } + is TypeIdentifier.Parameterised -> { + val reparameterizedType = typeIdentifier.copy(parameters = listOf(parameter.typeIdentifier)) + ACollection( + reparameterizedType.getLocalType(this::class.java.classLoader), + reparameterizedType, + parameter + ) + } + else -> throw IllegalStateException("Cannot parameterise $this") + } + } + + /** + * Represents a type whose underlying class is a map class such as [Map] with two type parameters. + * + * @param keyType [LocalTypeInformation] for the first resolved type parameter of the type, i.e. the type of its + * keys. [Unknown] if the type is erased. + * @param valueType [LocalTypeInformation] for the second resolved type parameter of the type, i.e. the type of its + * values. [Unknown] if the type is erased. + */ + data class AMap(override val observedType: Type, override val typeIdentifier: TypeIdentifier, + val keyType: LocalTypeInformation, val valueType: LocalTypeInformation) : LocalTypeInformation() { + val isErased: Boolean get() = typeIdentifier is TypeIdentifier.Erased + + fun withParameters(keyType: LocalTypeInformation, valueType: LocalTypeInformation): AMap = when(typeIdentifier) { + is TypeIdentifier.Erased -> { + val unerasedType = typeIdentifier.toParameterized(listOf(keyType.typeIdentifier, valueType.typeIdentifier)) + AMap( + unerasedType.getLocalType(this::class.java.classLoader), + unerasedType, + keyType, valueType) + } + is TypeIdentifier.Parameterised -> { + val reparameterizedType = typeIdentifier.copy(parameters = listOf(keyType.typeIdentifier, valueType.typeIdentifier)) + AMap( + reparameterizedType.getLocalType(this::class.java.classLoader), + reparameterizedType, + keyType, valueType + ) + } + else -> throw IllegalStateException("Cannot parameterise $this") + } + } +} + +/** + * Represents information about a constructor. + */ +data class LocalConstructorInformation( + val observedMethod: KFunction, + val parameters: List) { + val hasParameters: Boolean get() = parameters.isNotEmpty() +} + +/** + * Represents information about a constructor that is specifically to be used for evolution, and is potentially matched + * with a different set of properties to the regular constructor. + */ +data class EvolverConstructorInformation( + val constructor: LocalConstructorInformation, + val properties: Map) + +/** + * Represents information about a constructor parameter + */ +data class LocalConstructorParameterInformation( + val name: String, + val type: LocalTypeInformation, + val isMandatory: Boolean) + +private data class LocalTypeInformationPrettyPrinter(private val simplifyClassNames: Boolean, private val indent: Int = 0) { + + fun prettyPrint(typeInformation: LocalTypeInformation): String = + with(typeInformation) { + when (this) { + is LocalTypeInformation.Abstract -> + typeIdentifier.prettyPrint() + + printInheritsFrom(interfaces, superclass) + + indentAnd { printProperties(properties) } + is LocalTypeInformation.AnInterface -> + typeIdentifier.prettyPrint() + printInheritsFrom(interfaces) + is LocalTypeInformation.Composable -> typeIdentifier.prettyPrint() + + printConstructor(constructor) + + printInheritsFrom(interfaces, superclass) + + indentAnd { printProperties(properties) } + else -> typeIdentifier.prettyPrint() + } + } + + private fun printConstructor(constructor: LocalConstructorInformation) = + constructor.parameters.joinToString(", ", "(", ")") { + it.name + + ": " + it.type.typeIdentifier.prettyPrint(simplifyClassNames) + + (if (!it.isMandatory) "?" else "") + } + + private fun printInheritsFrom(interfaces: List, superclass: LocalTypeInformation? = null): String { + val parents = if (superclass == null || superclass == LocalTypeInformation.Top) interfaces.asSequence() + else sequenceOf(superclass) + interfaces.asSequence() + return if (!parents.iterator().hasNext()) "" + else parents.joinToString(", ", ": ", "") { it.typeIdentifier.prettyPrint(simplifyClassNames) } + } + + private fun printProperties(properties: Map) = + properties.entries.asSequence().sortedBy { it.key }.joinToString("\n", "\n", "") { + it.prettyPrint() + } + + private fun Map.Entry.prettyPrint(): String = + " ".repeat(indent) + key + + (if(!value.isMandatory) " (optional)" else "") + + (if (value.isCalculated) " (calculated)" else "") + + ": " + value.type.prettyPrint(simplifyClassNames) + + private inline fun indentAnd(block: LocalTypeInformationPrettyPrinter.() -> String) = + copy(indent = indent + 1).block() +} + diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt new file mode 100644 index 0000000000..88df121a43 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeInformationBuilder.kt @@ -0,0 +1,410 @@ +package net.corda.serialization.internal.model + +import net.corda.core.internal.isAbstractClass +import net.corda.core.internal.isConcreteClass +import net.corda.core.internal.kotlinObjectInstance +import net.corda.core.serialization.ConstructorForDeserialization +import net.corda.core.serialization.DeprecatedConstructorForDeserialization +import net.corda.core.utilities.contextLogger +import net.corda.serialization.internal.amqp.* +import java.io.NotSerializableException +import java.lang.reflect.Method +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.util.* +import kotlin.collections.LinkedHashMap +import kotlin.reflect.KFunction +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.internal.KotlinReflectionInternalError +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaConstructor +import kotlin.reflect.jvm.javaGetter +import kotlin.reflect.jvm.javaType + +/** + * Provides the logic for building instances of [LocalTypeInformation] by reflecting over local [Type]s. + * + * @param lookup The [LocalTypeLookup] to use to locate and register constructed [LocalTypeInformation]. + * @param resolutionContext The [Type] to use when attempting to resolve type variables. + * @param visited The [Set] of [TypeIdentifier]s already visited while building information for a given [Type]. Note that + * this is not a [MutableSet], as we want to be able to backtrack while traversing through the graph of related types, and + * will find it useful to revert to earlier states of knowledge about which types have been visited on a given branch. + */ +internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup, val resolutionContext: Type? = null, val visited: Set = emptySet()) { + + companion object { + private val logger = contextLogger() + } + + /** + * Recursively build [LocalTypeInformation] for the given [Type] and [TypeIdentifier] + */ + fun build(type: Type, typeIdentifier: TypeIdentifier): LocalTypeInformation = + if (typeIdentifier in visited) LocalTypeInformation.Cycle(type, typeIdentifier) { + LocalTypeInformationBuilder(lookup, resolutionContext).build(type, typeIdentifier) + } + else lookup.findOrBuild(type, typeIdentifier) { isOpaque -> + copy(visited = visited + typeIdentifier).buildIfNotFound(type, typeIdentifier, isOpaque) + } + + private fun resolveAndBuild(type: Type): LocalTypeInformation { + val resolved = type.resolveAgainstContext() + return build(resolved, TypeIdentifier.forGenericType(resolved, resolutionContext + ?: type)) + } + + private fun Type.resolveAgainstContext(): Type = + if (resolutionContext == null) this else resolveAgainst(resolutionContext) + + private fun buildIfNotFound(type: Type, typeIdentifier: TypeIdentifier, isOpaque: Boolean): LocalTypeInformation { + val rawType = type.asClass() + return when (typeIdentifier) { + is TypeIdentifier.TopType -> LocalTypeInformation.Top + is TypeIdentifier.UnknownType -> LocalTypeInformation.Unknown + is TypeIdentifier.Unparameterised, + is TypeIdentifier.Erased -> buildForClass(rawType, typeIdentifier, isOpaque) + is TypeIdentifier.ArrayOf -> { + LocalTypeInformation.AnArray( + type, + typeIdentifier, + resolveAndBuild(type.componentType())) + } + is TypeIdentifier.Parameterised -> buildForParameterised(rawType, type as ParameterizedType, typeIdentifier, isOpaque) + } + } + + private fun buildForClass(type: Class<*>, typeIdentifier: TypeIdentifier, isOpaque: Boolean): LocalTypeInformation = withContext(type) { + when { + Collection::class.java.isAssignableFrom(type) && + !EnumSet::class.java.isAssignableFrom(type) -> LocalTypeInformation.ACollection(type, typeIdentifier, LocalTypeInformation.Unknown) + Map::class.java.isAssignableFrom(type) -> LocalTypeInformation.AMap(type, typeIdentifier, LocalTypeInformation.Unknown, LocalTypeInformation.Unknown) + type.kotlin.javaPrimitiveType != null -> LocalTypeInformation.Atomic(type.kotlin.javaPrimitiveType!!, typeIdentifier) + type.isEnum -> LocalTypeInformation.AnEnum( + type, + typeIdentifier, + type.enumConstants.map { it.toString() }, + buildInterfaceInformation(type)) + type.kotlinObjectInstance != null -> LocalTypeInformation.Singleton( + type, + typeIdentifier, + buildSuperclassInformation(type), + buildInterfaceInformation(type)) + type.isInterface -> buildInterface(type, typeIdentifier, emptyList()) + type.isAbstractClass -> buildAbstract(type, typeIdentifier, emptyList()) + else -> when { + isOpaque -> LocalTypeInformation.Opaque(type, typeIdentifier) { + buildNonAtomic(type, type, typeIdentifier, emptyList()) + } + else -> buildNonAtomic(type, type, typeIdentifier, emptyList()) + } + } + } + + private fun buildForParameterised( + rawType: Class<*>, + type: ParameterizedType, + typeIdentifier: TypeIdentifier.Parameterised, + isOpaque: Boolean): LocalTypeInformation = withContext(type) { + when { + Collection::class.java.isAssignableFrom(rawType) && + !EnumSet::class.java.isAssignableFrom(rawType) -> + LocalTypeInformation.ACollection(type, typeIdentifier, buildTypeParameterInformation(type)[0]) + Map::class.java.isAssignableFrom(rawType) -> { + val (keyType, valueType) = buildTypeParameterInformation(type) + LocalTypeInformation.AMap(type, typeIdentifier, keyType, valueType) + } + rawType.isInterface -> buildInterface(type, typeIdentifier, buildTypeParameterInformation(type)) + rawType.isAbstractClass -> buildAbstract(type, typeIdentifier, buildTypeParameterInformation(type)) + else -> when { + isOpaque -> LocalTypeInformation.Opaque(rawType, typeIdentifier) { + buildNonAtomic(rawType, type, typeIdentifier, buildTypeParameterInformation(type)) + } + else -> buildNonAtomic(rawType, type, typeIdentifier, buildTypeParameterInformation(type)) + } + } + } + + private fun buildAbstract(type: Type, typeIdentifier: TypeIdentifier, + typeParameters: List): LocalTypeInformation.Abstract = + LocalTypeInformation.Abstract( + type, + typeIdentifier, + buildReadOnlyProperties(type.asClass()), + buildSuperclassInformation(type), + buildInterfaceInformation(type), + typeParameters) + + private fun buildInterface(type: Type, typeIdentifier: TypeIdentifier, + typeParameters: List): LocalTypeInformation.AnInterface = + LocalTypeInformation.AnInterface( + type, + typeIdentifier, + buildReadOnlyProperties(type.asClass()), + buildInterfaceInformation(type), + typeParameters) + + private inline fun withContext(newContext: Type, block: LocalTypeInformationBuilder.() -> T): T = + copy(resolutionContext = newContext).run(block) + + /** + * Build a non-atomic type, which is either [Composable] or [NonComposable]. + * + * Composability is a transitive property: a type is [Composable] iff it has a unique deserialization constructor _and_ + * all of its property types are also [Composable]. If not, the type is [NonComposable], meaning we cannot deserialize + * it without a custom serializer (in which case it should normally have been flagged as [Opaque]). + * + * Rather than throwing an exception if a type is [NonComposable], we capture its type information so that it can + * still be used to _serialize_ values, or as the basis for deciding on an evolution strategy. + */ + private fun buildNonAtomic(rawType: Class<*>, type: Type, typeIdentifier: TypeIdentifier, typeParameterInformation: List): LocalTypeInformation { + val superclassInformation = buildSuperclassInformation(type) + val interfaceInformation = buildInterfaceInformation(type) + val observedConstructor = constructorForDeserialization(type) + + if (observedConstructor == null) { + logger.warn("No unique deserialisation constructor found for class $rawType, type is marked as non-composable") + return LocalTypeInformation.NonComposable(type, typeIdentifier, null, buildReadOnlyProperties(rawType), + superclassInformation, interfaceInformation, typeParameterInformation) + } + + val constructorInformation = buildConstructorInformation(type, observedConstructor) + val properties = buildObjectProperties(rawType, constructorInformation) + + val hasNonComposableProperties = properties.values.any { it.type is LocalTypeInformation.NonComposable } + + if (!propertiesSatisfyConstructor(constructorInformation, properties) || hasNonComposableProperties) { + if (hasNonComposableProperties) { + logger.warn("Type ${type.typeName} has non-composable properties and has been marked as non-composable") + } else { + logger.warn("Properties of type ${type.typeName} do not satisfy its constructor, type has been marked as non-composable") + } + return LocalTypeInformation.NonComposable(type, typeIdentifier, constructorInformation, properties, superclassInformation, + interfaceInformation, typeParameterInformation) + } + + val evolverConstructors = evolverConstructors(type).map { ctor -> + val constructorInformation = buildConstructorInformation(type, ctor) + val evolverProperties = buildObjectProperties(rawType, constructorInformation) + EvolverConstructorInformation(constructorInformation, evolverProperties) + } + + return LocalTypeInformation.Composable(type, typeIdentifier, constructorInformation, evolverConstructors, properties, + superclassInformation, interfaceInformation, typeParameterInformation) + } + + // Can we supply all of the mandatory constructor parameters using values addressed by readable properties? + private fun propertiesSatisfyConstructor(constructorInformation: LocalConstructorInformation, properties: Map): Boolean { + if (!constructorInformation.hasParameters) return true + + val indicesAddressedByProperties = properties.values.asSequence().mapNotNull { + when (it) { + is LocalPropertyInformation.ConstructorPairedProperty -> it.constructorSlot.parameterIndex + is LocalPropertyInformation.PrivateConstructorPairedProperty -> it.constructorSlot.parameterIndex + else -> null + } + }.toSet() + + return (0 until constructorInformation.parameters.size).none { index -> + constructorInformation.parameters[index].isMandatory && index !in indicesAddressedByProperties + } + } + + private fun buildSuperclassInformation(type: Type): LocalTypeInformation = + resolveAndBuild(type.asClass().genericSuperclass) + + private fun buildInterfaceInformation(type: Type) = + type.allInterfaces.asSequence().mapNotNull { + if (it == type) return@mapNotNull null + resolveAndBuild(it) + }.toList() + + private val Type.allInterfaces: Set get() = exploreType(this) + + private fun exploreType(type: Type, interfaces: MutableSet = LinkedHashSet()): MutableSet { + val clazz = type.asClass() + + if (clazz.isInterface) { + // Ignore classes we've already seen, and stop exploring once we reach an excluded type. + if (clazz in interfaces || lookup.isExcluded(clazz)) return interfaces + else interfaces += type + } + + clazz.genericInterfaces.forEach { exploreType(it.resolveAgainstContext(), interfaces) } + if (clazz.genericSuperclass != null) exploreType(clazz.genericSuperclass.resolveAgainstContext(), interfaces) + + return interfaces + } + + private fun buildReadOnlyProperties(rawType: Class<*>): Map = + rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + if (descriptor.field == null || descriptor.getter == null) null + else { + val paramType = (descriptor.getter.genericReturnType).resolveAgainstContext() + val paramTypeInformation = build(paramType, TypeIdentifier.forGenericType(paramType, resolutionContext + ?: rawType)) + val isMandatory = paramType.asClass().isPrimitive || !descriptor.getter.returnsNullable() + name to LocalPropertyInformation.ReadOnlyProperty(descriptor.getter, paramTypeInformation, isMandatory) + } + }.sortedBy { (name, _) -> name }.toMap(LinkedHashMap()) + + private fun buildObjectProperties(rawType: Class<*>, constructorInformation: LocalConstructorInformation): Map = + (calculatedProperties(rawType) + nonCalculatedProperties(rawType, constructorInformation)) + .sortedBy { (name, _) -> name } + .toMap(LinkedHashMap()) + + private fun nonCalculatedProperties(rawType: Class<*>, constructorInformation: LocalConstructorInformation): Sequence> = + if (constructorInformation.hasParameters) getConstructorPairedProperties(constructorInformation, rawType) + else getterSetterProperties(rawType) + + private fun getConstructorPairedProperties(constructorInformation: LocalConstructorInformation, rawType: Class<*>): Sequence> { + val constructorParameterIndices = constructorInformation.parameters.asSequence().mapIndexed { index, parameter -> + parameter.name to index + }.toMap() + + return rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + val property = makeConstructorPairedProperty(constructorParameterIndices, name, descriptor, constructorInformation) + if (property == null) null else name to property + } + } + + private fun makeConstructorPairedProperty(constructorParameterIndices: Map, + name: String, + descriptor: PropertyDescriptor, + constructorInformation: LocalConstructorInformation): LocalPropertyInformation? { + val constructorIndex = constructorParameterIndices[name] ?: + // In some very rare cases we have a constructor parameter matched by a getter with no backing field, + // and cannot infer whether the property name should be capitalised or not. + constructorParameterIndices[name.decapitalize()] ?: return null + + if (descriptor.getter == null) { + if (descriptor.field == null) return null + val paramType = descriptor.field.genericType + val paramTypeInformation = resolveAndBuild(paramType) + + return LocalPropertyInformation.PrivateConstructorPairedProperty( + descriptor.field, + ConstructorSlot(constructorIndex, constructorInformation), + paramTypeInformation, + constructorInformation.parameters[constructorIndex].isMandatory) + } + + val paramType = descriptor.getter.genericReturnType + val paramTypeInformation = resolveAndBuild(paramType) + + return LocalPropertyInformation.ConstructorPairedProperty( + descriptor.getter, + ConstructorSlot(constructorIndex, constructorInformation), + paramTypeInformation, + descriptor.getter.returnType.isPrimitive || + !descriptor.getter.returnsNullable()) + } + + private fun getterSetterProperties(rawType: Class<*>): Sequence> = + rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + if (descriptor.getter == null || descriptor.setter == null || descriptor.field == null) null + else { + val paramType = descriptor.getter.genericReturnType + val paramTypeInformation = resolveAndBuild(paramType) + val isMandatory = paramType.asClass().isPrimitive || !descriptor.getter.returnsNullable() + + name to LocalPropertyInformation.GetterSetterProperty( + descriptor.getter, + descriptor.setter, + paramTypeInformation, + isMandatory) + } + } + + private fun calculatedProperties(rawType: Class<*>): Sequence> = + rawType.calculatedPropertyDescriptors().asSequence().map { (name, v) -> + val paramType = v.getter!!.genericReturnType + val paramTypeInformation = resolveAndBuild(paramType) + val isMandatory = paramType.asClass().isPrimitive || !v.getter.returnsNullable() + + name to LocalPropertyInformation.CalculatedProperty(v.getter, paramTypeInformation, isMandatory) + } + + private fun buildTypeParameterInformation(type: ParameterizedType): List = + type.actualTypeArguments.map { + resolveAndBuild(it) + } + + private fun buildConstructorInformation(type: Type, observedConstructor: KFunction): LocalConstructorInformation { + if (observedConstructor.javaConstructor?.parameters?.getOrNull(0)?.name == "this$0") + throw NotSerializableException("Type '${type.typeName} has synthetic fields and is likely a nested inner class.") + + return LocalConstructorInformation(observedConstructor, observedConstructor.parameters.map { + val parameterType = it.type.javaType + LocalConstructorParameterInformation( + it.name ?: throw IllegalStateException("Unnamed parameter in constructor $observedConstructor"), + resolveAndBuild(parameterType), + parameterType.asClass().isPrimitive || !it.type.isMarkedNullable) + }) + } +} + +private fun Method.returnsNullable(): Boolean = try { + val returnTypeString = this.declaringClass.kotlin.memberProperties.firstOrNull { + it.javaGetter == this + }?.returnType?.toString() ?: "?" + + returnTypeString.endsWith('?') || returnTypeString.endsWith('!') +} catch (e: KotlinReflectionInternalError) { + // This might happen for some types, e.g. kotlin.Throwable? - the root cause of the issue + // is: https://youtrack.jetbrains.com/issue/KT-13077 + // TODO: Revisit this when Kotlin issue is fixed. + + true +} + +/** + * Code for finding the unique constructor we will use for deserialization. + * + * If any constructor is uniquely annotated with [@ConstructorForDeserialization], then that constructor is chosen. + * An error is reported if more than one constructor is annotated. + * + * Otherwise, if there is a Kotlin primary constructor, it selects that, and if not it selects either the unique + * constructor or, if there are two and one is the default no-argument constructor, the non-default constructor. + */ +private fun constructorForDeserialization(type: Type): KFunction? { + val clazz = type.asClass() + if (!clazz.isConcreteClass || clazz.isSynthetic) return null + + val kotlinCtors = clazz.kotlin.constructors + + val annotatedCtors = kotlinCtors.filter { it.findAnnotation() != null } + if (annotatedCtors.size > 1) return null + if (annotatedCtors.size == 1) return annotatedCtors.first().apply { isAccessible = true } + + val defaultCtor = kotlinCtors.firstOrNull { it.parameters.isEmpty() } + val nonDefaultCtors = kotlinCtors.filter { it != defaultCtor } + + val preferredCandidate = clazz.kotlin.primaryConstructor ?: + when(nonDefaultCtors.size) { + 1 -> nonDefaultCtors.first() + 0 -> defaultCtor + else -> null + } ?: return null + + return try { + preferredCandidate.apply { isAccessible = true } + } catch (e: SecurityException) { + null + } +} + +private fun evolverConstructors(type: Type): List> { + val clazz = type.asClass() + if (!clazz.isConcreteClass || clazz.isSynthetic) return emptyList() + + return clazz.kotlin.constructors.asSequence() + .mapNotNull { + val version = it.findAnnotation()?.version + if (version == null) null else version to it + } + .sortedBy { (version, ctor) -> version } + .map { (version, ctor) -> ctor.apply { isAccessible = true} } + .toList() +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt new file mode 100644 index 0000000000..9acdacb71e --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/LocalTypeModel.kt @@ -0,0 +1,91 @@ +package net.corda.serialization.internal.model + +import net.corda.core.serialization.ClassWhitelist +import net.corda.serialization.internal.amqp.* +import java.lang.reflect.* + +/** + * Provides a means for looking up [LocalTypeInformation] by [Type] and [TypeIdentifier], falling back to building it + * if the lookup can't supply it. + * + * The purpose of this class is to make a registry of [LocalTypeInformation] usable by a [LocalTypeInformationBuilder] that + * recursively builds [LocalTypeInformation] for all of the types visible by traversing the DAG of related types of a given + * [Type]. + */ +interface LocalTypeLookup { + + /** + * Either return the [LocalTypeInformation] held in the registry for the given [Type] and [TypeIdentifier] or, if + * no such information is registered, call the supplied builder to construct the type information, add it to the + * registry and then return it. + */ + fun findOrBuild(type: Type, typeIdentifier: TypeIdentifier, builder: (Boolean) -> LocalTypeInformation): LocalTypeInformation + + /** + * Indicates whether a type should be excluded from lists of interfaces associated with inspected types, i.e. + * because it is not whitelisted. + */ + fun isExcluded(type: Type): Boolean +} + +/** + * A [LocalTypeModel] maintains a registry of [LocalTypeInformation] for all [Type]s which have been observed within a + * given classloader context. + */ +interface LocalTypeModel { + /** + * Look for a [Type] in the registry, and return its associated [LocalTypeInformation] if found. If the [Type] is + * not in the registry, build [LocalTypeInformation] for that type, using this [LocalTypeModel] as the [LocalTypeLookup] + * for recursively resolving dependencies, place it in the registry, and return it. + * + * @param type The [Type] to get [LocalTypeInformation] for. + */ + fun inspect(type: Type): LocalTypeInformation + + /** + * Get [LocalTypeInformation] directly from the registry by [TypeIdentifier], returning null if no type information + * is registered for that identifier. + */ + operator fun get(typeIdentifier: TypeIdentifier): LocalTypeInformation? +} + +/** + * A [LocalTypeLookup] that is configurable with [LocalTypeModelConfiguration], which controls which types are seen as "opaque" + * and which are "excluded" (see docs for [LocalTypeModelConfiguration] for explanation of these terms. + * + * @param typeModelConfiguration Configuration controlling the behaviour of the [LocalTypeModel]'s type inspection. + */ +class ConfigurableLocalTypeModel(private val typeModelConfiguration: LocalTypeModelConfiguration): LocalTypeModel, LocalTypeLookup { + + private val typeInformationCache = DefaultCacheProvider.createCache() + + override fun isExcluded(type: Type): Boolean = typeModelConfiguration.isExcluded(type) + + override fun inspect(type: Type): LocalTypeInformation = LocalTypeInformation.forType(type, this) + + override fun findOrBuild(type: Type, typeIdentifier: TypeIdentifier, builder: (Boolean) -> LocalTypeInformation): LocalTypeInformation = + this[typeIdentifier] ?: builder(typeModelConfiguration.isOpaque(type)).apply { + typeInformationCache.putIfAbsent(typeIdentifier, this) + } + + override operator fun get(typeIdentifier: TypeIdentifier): LocalTypeInformation? = typeInformationCache[typeIdentifier] +} + +/** + * Configuration which controls how a [LocalTypeModel] inspects classes to build [LocalTypeInformation]. + */ +interface LocalTypeModelConfiguration { + /** + * [Type]s which are flagged as "opaque" are converted into instances of [LocalTypeInformation.Opaque] without + * further inspection - the type model doesn't attempt to inspect their superclass/interface hierarchy, locate + * constructors or enumerate their properties. Usually this will be because the type is handled by a custom + * serializer, so we don't need detailed information about it to help us build one. + */ + fun isOpaque(type: Type): Boolean + + /** + * [Type]s which are excluded are silently omitted from the superclass/interface hierarchy of other types' + * [LocalTypeInformation], usually because they are not included in a whitelist. + */ + fun isExcluded(type: Type): Boolean +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeCarpenter.kt new file mode 100644 index 0000000000..75e4b9363d --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeCarpenter.kt @@ -0,0 +1,82 @@ +package net.corda.serialization.internal.model + +import net.corda.serialization.internal.amqp.asClass +import net.corda.serialization.internal.carpenter.* +import java.io.NotSerializableException +import java.lang.reflect.Type + +/** + * Constructs [Type]s using [RemoteTypeInformation]. + */ +interface RemoteTypeCarpenter { + fun carpent(typeInformation: RemoteTypeInformation): Type +} + +/** + * A [RemoteTypeCarpenter] that converts [RemoteTypeInformation] into [Schema] objects for the [ClassCarpenter] to use. + */ +class SchemaBuildingRemoteTypeCarpenter(private val carpenter: ClassCarpenter): RemoteTypeCarpenter { + + private val classLoader: ClassLoader get() = carpenter.classloader + + override fun carpent(typeInformation: RemoteTypeInformation): Type { + try { + when (typeInformation) { + is RemoteTypeInformation.AnInterface -> typeInformation.carpentInterface() + is RemoteTypeInformation.Composable -> typeInformation.carpentComposable() + is RemoteTypeInformation.AnEnum -> typeInformation.carpentEnum() + else -> { + } // Anything else, such as arrays, will be taken care of by the above + } + } catch (e: ClassCarpenterException) { + throw NotSerializableException("${typeInformation.typeIdentifier.name}: ${e.message}") + } + return typeInformation.typeIdentifier.getLocalType(classLoader) + } + + private val RemoteTypeInformation.erasedLocalClass get() = typeIdentifier.getLocalType(classLoader).asClass() + + private fun RemoteTypeInformation.AnInterface.carpentInterface() { + val fields = getFields(typeIdentifier.name, properties) + + val schema = CarpenterSchemaFactory.newInstance( + name = typeIdentifier.name, + fields = fields, + interfaces = getInterfaces(typeIdentifier.name, interfaces), + isInterface = true) + carpenter.build(schema) + } + + private fun RemoteTypeInformation.Composable.carpentComposable() { + val fields = getFields(typeIdentifier.name, properties) + + val schema = CarpenterSchemaFactory.newInstance( + name = typeIdentifier.name, + fields = fields, + interfaces = getInterfaces(typeIdentifier.name, interfaces), + isInterface = false) + carpenter.build(schema) + } + + private fun getFields(ownerName: String, properties: Map) = + properties.mapValues { (name, property) -> + try { + FieldFactory.newInstance(property.isMandatory, name, property.type.erasedLocalClass) + } catch (e: ClassNotFoundException) { + throw UncarpentableException(ownerName, name, property.type.typeIdentifier.name) + } + } + + private fun getInterfaces(ownerName: String, interfaces: List): List> = + interfaces.map { + try { + it.erasedLocalClass + } catch (e: ClassNotFoundException) { + throw UncarpentableException(ownerName, "[interface]", it.typeIdentifier.name) + } + } + + private fun RemoteTypeInformation.AnEnum.carpentEnum() { + carpenter.build(EnumSchema(name = typeIdentifier.name, fields = members.associate { it to EnumField() })) + } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeInformation.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeInformation.kt new file mode 100644 index 0000000000..93c0e72307 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/RemoteTypeInformation.kt @@ -0,0 +1,196 @@ +package net.corda.serialization.internal.model + +import net.corda.serialization.internal.amqp.Transform +import net.corda.serialization.internal.amqp.TransformTypes +import java.util.* + +typealias TypeDescriptor = String + +/** + * Represents a property of a remotely-defined type. + * + * @param type The type of the property. + * @param isMandatory Whether the property is mandatory (i.e. non-nullable). + */ +data class RemotePropertyInformation(val type: RemoteTypeInformation, val isMandatory: Boolean) + +/** + * The [RemoteTypeInformation] extracted from a remote data source's description of its type schema captures the + * information contained in that schema in a form similar to that of [LocalTypeInformation], but stripped of any + * reference to local type information such as [Type]s, [Method]s, constructors and so on. + * + * It has two main uses: + * + * 1) Comparison with [LocalTypeInformation] to determine compatibility and whether type evolution should be attempted. + * 2) Providing a specification to a [ClassCarpenter] that will synthesize a [Type] at runtime. + * + * A [TypeLoader] knows how to load types described by [RemoteTypeInformation], using a [ClassCarpenter] to build + * synthetic types where needed, so that every piece of [RemoteTypeInformation] is matched to a corresponding local + * [Type] for which [LocalTypeInformation] can be generated. Once we have both [RemoteTypeInformation] and + * [LocalTypeInformation] in hand, we can make decisions about the compatibility between the remote and local type + * schemas. + * + * In the future, it may make sense to generate type schema information by reflecting [LocalTypeInformation] into + * [RemoteTypeInformation]. + * + * Each piece of [RemoteTypeInformation] has both a [TypeIdentifier], which is not guaranteed to be globally uniquely + * identifying, and a [TypeDescriptor], which is. + * + * [TypeIdentifier]s are not globally uniquely identifying because + * multiple remote sources may define their own versions of the same type, with potentially different properties. However, + * they are unique to a given message-exchange session, and are used as unique references for types within the type + * schema associated with a given message. + * + * [TypeDescriptor]s are obtained by "fingerprinting" [LocalTypeInformation], and represent a hashed digest of all of + * the information locally available about a type. If a remote [TypeDescriptor] matches that of a local type, then we + * know that they are fully schema-compatible. However, it is possible for two types to diverge due to inconsistent + * erasure, so that they will have different [TypeDescriptor]s, and yet represent the "same" type for purposes of + * serialisation. In this case, we will determine compatibility based on comparison of the [RemoteTypeInformation]'s + * type graph with that of the [LocalTypeInformation] which reflects it. + */ +sealed class RemoteTypeInformation { + + /** + * The globally-unique [TypeDescriptor] of the represented type. + */ + abstract val typeDescriptor: TypeDescriptor + + /** + * The [TypeIdentifier] of the represented type. + */ + abstract val typeIdentifier: TypeIdentifier + + /** + * Obtain a multi-line, recursively-indented representation of this type information. + * + * @param simplifyClassNames By default, class names are printed as their "simple" class names, i.e. "String" instead + * of "java.lang.String". If this is set to `false`, then the full class name will be printed instead. + */ + fun prettyPrint(simplifyClassNames: Boolean = true): String = + RemoteTypeInformationPrettyPrinter(simplifyClassNames).prettyPrint(this) + + /** + * The [RemoteTypeInformation] corresponding to an unbounded wildcard ([TypeIdentifier.UnknownType]) + */ + object Unknown : RemoteTypeInformation() { + override val typeDescriptor = "?" + override val typeIdentifier = TypeIdentifier.UnknownType + } + + /** + * The [RemoteTypeInformation] corresponding to [java.lang.Object] / [Any] ([TypeIdentifier.TopType]) + */ + object Top : RemoteTypeInformation() { + override val typeDescriptor = "*" + override val typeIdentifier = TypeIdentifier.TopType + } + + /** + * The [RemoteTypeInformation] emitted if we hit a cycle while traversing the graph of related types. + */ + data class Cycle(override val typeIdentifier: TypeIdentifier, private val _follow: () -> RemoteTypeInformation) : RemoteTypeInformation() { + override val typeDescriptor = typeIdentifier.name + val follow: RemoteTypeInformation get() = _follow() + + override fun equals(other: Any?): Boolean = other is Cycle && other.typeIdentifier == typeIdentifier + override fun hashCode(): Int = typeIdentifier.hashCode() + override fun toString(): String = "Cycle($typeIdentifier)" + } + + /** + * Representation of a simple unparameterised type. + */ + data class Unparameterised(override val typeDescriptor: TypeDescriptor, override val typeIdentifier: TypeIdentifier) : RemoteTypeInformation() + + /** + * Representation of a type with type parameters. + * + * @param typeParameters The type parameters of the type. + */ + data class Parameterised(override val typeDescriptor: TypeDescriptor, override val typeIdentifier: TypeIdentifier, val typeParameters: List) : RemoteTypeInformation() + + /** + * Representation of an array of some other type. + * + * @param componentType The component type of the array. + */ + data class AnArray(override val typeDescriptor: TypeDescriptor, override val typeIdentifier: TypeIdentifier, val componentType: RemoteTypeInformation) : RemoteTypeInformation() + + /** + * Representation of an Enum type. + * + * @param members The members of the enum. + */ + data class AnEnum(override val typeDescriptor: TypeDescriptor, + override val typeIdentifier: TypeIdentifier, + val members: List, + val transforms: EnumTransforms) : RemoteTypeInformation() + + /** + * Representation of an interface. + * + * @param properties The properties (i.e. "getter" methods) of the interface. + * @param interfaces The interfaces extended by the interface. + * @param typeParameters The type parameters of the interface. + */ + data class AnInterface(override val typeDescriptor: TypeDescriptor, override val typeIdentifier: TypeIdentifier, val properties: Map, val interfaces: List, val typeParameters: List) : RemoteTypeInformation() + + /** + * Representation of a concrete POJO-like class. + * + * @param properties The properties of the class. + * @param interfaces The interfaces extended by the class. + * @param typeParameters The type parameters of the class. + */ + data class Composable( + override val typeDescriptor: TypeDescriptor, + override val typeIdentifier: TypeIdentifier, + val properties: Map, + val interfaces: List, + val typeParameters: List) : RemoteTypeInformation() +} + +private data class RemoteTypeInformationPrettyPrinter(private val simplifyClassNames: Boolean = true, private val indent: Int = 0) { + + fun prettyPrint(remoteTypeInformation: RemoteTypeInformation): String = with(remoteTypeInformation){ + when (this) { + is RemoteTypeInformation.AnInterface -> typeIdentifier.prettyPrint(simplifyClassNames) + + printInterfaces(interfaces) + + indentAnd { printProperties(properties) } + is RemoteTypeInformation.Composable -> typeIdentifier.prettyPrint(simplifyClassNames) + + printInterfaces(interfaces) + + indentAnd { printProperties(properties) } + is RemoteTypeInformation.AnEnum -> typeIdentifier.prettyPrint(simplifyClassNames) + + members.joinToString("|", "(", ")") + else -> typeIdentifier.prettyPrint(simplifyClassNames) + } + } + + private inline fun indentAnd(block: RemoteTypeInformationPrettyPrinter.() -> String) = + copy(indent = indent + 1).block() + + private fun printInterfaces(interfaces: List) = + if (interfaces.isEmpty()) "" + else interfaces.joinToString(", ", ": ", "") { + it.typeIdentifier.prettyPrint(simplifyClassNames) + } + + private fun printProperties(properties: Map) = + properties.entries.sortedBy { it.key }.joinToString("\n", "\n", "") { + it.prettyPrint() + } + + private fun Map.Entry.prettyPrint(): String = + " ".repeat(indent) + key + + (if(!value.isMandatory) " (optional)" else "") + + ": " + value.type.prettyPrint(simplifyClassNames) +} + +data class EnumTransforms(val defaults: Map, val renames: Map) { + + val size: Int get() = defaults.size + renames.size + + companion object { + val empty = EnumTransforms(emptyMap(), emptyMap()) + } +} \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt index 3e4816b0c6..95e4a6132a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeIdentifier.kt @@ -1,7 +1,16 @@ package net.corda.serialization.internal.model import com.google.common.reflect.TypeToken +import net.corda.serialization.internal.amqp.asClass +import java.io.NotSerializableException import java.lang.reflect.* +import java.util.* + +/** + * Thrown if a [TypeIdentifier] is incompatible with the local [Type] to which it refers, + * i.e. if the number of type parameters does not match. + */ +class IncompatibleTypeIdentifierException(message: String) : NotSerializableException(message) /** * Used as a key for retrieving cached type information. We need slightly more information than the bare classname, @@ -21,18 +30,30 @@ sealed class TypeIdentifier { */ abstract val name: String + /** + * Obtain the local type matching this identifier + * + * @param classLoader The classloader to use to load the type. + * @throws ClassNotFoundException if the type or any of its parameters cannot be loaded. + * @throws IncompatibleTypeIdentifierException if the type identifier is incompatible with the locally-defined type + * to which it refers. + */ + abstract fun getLocalType(classLoader: ClassLoader = ClassLoader.getSystemClassLoader()): Type + + open val erased: TypeIdentifier get() = this + /** * Obtain a nicely-formatted representation of the identified type, for help with debugging. */ fun prettyPrint(simplifyClassNames: Boolean = true): String = when(this) { - is TypeIdentifier.Unknown -> "?" - is TypeIdentifier.Top -> "*" + is TypeIdentifier.UnknownType -> "?" + is TypeIdentifier.TopType -> "*" is TypeIdentifier.Unparameterised -> name.simplifyClassNameIfRequired(simplifyClassNames) is TypeIdentifier.Erased -> "${name.simplifyClassNameIfRequired(simplifyClassNames)} (erased)" - is TypeIdentifier.ArrayOf -> "${componentType.prettyPrint()}[]" + is TypeIdentifier.ArrayOf -> "${componentType.prettyPrint(simplifyClassNames)}[]" is TypeIdentifier.Parameterised -> name.simplifyClassNameIfRequired(simplifyClassNames) + parameters.joinToString(", ", "<", ">") { - it.prettyPrint() + it.prettyPrint(simplifyClassNames) } } @@ -46,10 +67,10 @@ sealed class TypeIdentifier { * @param type The class to get a [TypeIdentifier] for. */ fun forClass(type: Class<*>): TypeIdentifier = when { - type.name == "java.lang.Object" -> Top + type.name == "java.lang.Object" -> TopType type.isArray -> ArrayOf(forClass(type.componentType)) type.typeParameters.isEmpty() -> Unparameterised(type.name) - else -> Erased(type.name) + else -> Erased(type.name, type.typeParameters.size) } /** @@ -63,45 +84,92 @@ sealed class TypeIdentifier { * class implementing a parameterised interface and specifying values for type variables which are referred to * by methods defined in the interface. */ - fun forGenericType(type: Type, resolutionContext: Type = type): TypeIdentifier = when(type) { - is ParameterizedType -> Parameterised((type.rawType as Class<*>).name, type.actualTypeArguments.map { - forGenericType(it.resolveAgainst(resolutionContext)) - }) - is Class<*> -> forClass(type) - is GenericArrayType -> ArrayOf(forGenericType(type.genericComponentType.resolveAgainst(resolutionContext))) - else -> Unknown - } + fun forGenericType(type: Type, resolutionContext: Type = type): TypeIdentifier = + when(type) { + is ParameterizedType -> Parameterised( + (type.rawType as Class<*>).name, + type.ownerType?.let { forGenericType(it) }, + type.actualTypeArguments.map { + forGenericType(it.resolveAgainst(resolutionContext)) + }) + is Class<*> -> forClass(type) + is GenericArrayType -> ArrayOf(forGenericType(type.genericComponentType.resolveAgainst(resolutionContext))) + is WildcardType -> type.upperBound.let { if (it == type) UnknownType else forGenericType(it) } + else -> UnknownType + } } /** * The [TypeIdentifier] of [Any] / [java.lang.Object]. */ - object Top : TypeIdentifier() { + object TopType : TypeIdentifier() { override val name get() = "*" - override fun toString() = "Top" + override fun getLocalType(classLoader: ClassLoader): Type = Any::class.java + override fun toString() = "TopType" + } + + private object UnboundedWildcardType : WildcardType { + override fun getLowerBounds(): Array = emptyArray() + override fun getUpperBounds(): Array = arrayOf(Any::class.java) + override fun toString() = "?" } /** * The [TypeIdentifier] of an unbounded wildcard. */ - object Unknown : TypeIdentifier() { + object UnknownType : TypeIdentifier() { override val name get() = "?" - override fun toString() = "Unknown" + override fun getLocalType(classLoader: ClassLoader): Type = UnboundedWildcardType + override fun toString() = "UnknownType" } /** * Identifies a class with no type parameters. */ data class Unparameterised(override val name: String) : TypeIdentifier() { + + companion object { + private val primitives = listOf( + Byte::class, + Boolean:: class, + Char::class, + Int::class, + Short::class, + Long::class, + Float::class, + Double::class).associate { + it.javaPrimitiveType!!.name to it.javaPrimitiveType + } + } override fun toString() = "Unparameterised($name)" + override fun getLocalType(classLoader: ClassLoader): Type = primitives[name] ?: classLoader.loadClass(name) + + val isPrimitive get() = name in primitives } /** * Identifies a parameterised class such as List, for which we cannot obtain the type parameters at runtime * because they have been erased. */ - data class Erased(override val name: String) : TypeIdentifier() { + data class Erased(override val name: String, val erasedParameterCount: Int) : TypeIdentifier() { + fun toParameterized(parameters: List): TypeIdentifier { + if (parameters.size != erasedParameterCount) throw IncompatibleTypeIdentifierException( + "Erased type $name takes $erasedParameterCount parameters, but ${parameters.size} supplied" + ) + return Parameterised(name, null, parameters) + } + override fun toString() = "Erased($name)" + + override fun getLocalType(classLoader: ClassLoader): Type = classLoader.loadClass(name) + } + + private class ReconstitutedGenericArrayType(private val componentType: Type) : GenericArrayType { + override fun getGenericComponentType(): Type = componentType + override fun toString() = "$componentType[]" + override fun equals(other: Any?): Boolean = + other is GenericArrayType && componentType == other.genericComponentType + override fun hashCode(): Int = Objects.hashCode(componentType) } /** @@ -112,6 +180,30 @@ sealed class TypeIdentifier { data class ArrayOf(val componentType: TypeIdentifier) : TypeIdentifier() { override val name get() = componentType.name + "[]" override fun toString() = "ArrayOf(${componentType.prettyPrint()})" + override fun getLocalType(classLoader: ClassLoader): Type { + val component = componentType.getLocalType(classLoader) + return when (componentType) { + is Parameterised -> ReconstitutedGenericArrayType(component) + else -> java.lang.reflect.Array.newInstance(component.asClass(), 0).javaClass + } + } + } + + private class ReconstitutedParameterizedType( + private val _rawType: Type, + private val _ownerType: Type?, + private val _actualTypeArguments: Array) : ParameterizedType { + override fun getRawType(): Type = _rawType + override fun getOwnerType(): Type? = _ownerType + override fun getActualTypeArguments(): Array = _actualTypeArguments + override fun toString(): String = TypeIdentifier.forGenericType(this).prettyPrint(false) + override fun equals(other: Any?): Boolean = + other is ParameterizedType && + other.rawType == rawType && + other.ownerType == ownerType && + Arrays.equals(other.actualTypeArguments, actualTypeArguments) + override fun hashCode(): Int = + Arrays.hashCode(actualTypeArguments) xor Objects.hashCode(ownerType) xor Objects.hashCode(rawType) } /** @@ -119,8 +211,25 @@ sealed class TypeIdentifier { * * @param parameters [TypeIdentifier]s for each of the resolved type parameter values of this type. */ - data class Parameterised(override val name: String, val parameters: List) : TypeIdentifier() { + data class Parameterised(override val name: String, val owner: TypeIdentifier?, val parameters: List) : TypeIdentifier() { + /** + * Get the type-erased equivalent of this type. + */ + override val erased: TypeIdentifier get() = Erased(name, parameters.size) + override fun toString() = "Parameterised(${prettyPrint()})" + override fun getLocalType(classLoader: ClassLoader): Type { + val rawType = classLoader.loadClass(name) + if (rawType.typeParameters.size != parameters.size) { + throw IncompatibleTypeIdentifierException( + "Class $rawType expects ${rawType.typeParameters.size} type arguments, " + + "but type ${this.prettyPrint(false)} has ${parameters.size}") + } + return ReconstitutedParameterizedType( + rawType, + owner?.getLocalType(classLoader), + parameters.map { it.getLocalType(classLoader) }.toTypedArray()) + } } } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt new file mode 100644 index 0000000000..ca541f6102 --- /dev/null +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeLoader.kt @@ -0,0 +1,61 @@ +package net.corda.serialization.internal.model + +import net.corda.serialization.internal.carpenter.* +import java.io.NotSerializableException +import java.lang.ClassCastException +import java.lang.reflect.Type + +/** + * A [TypeLoader] obtains local types whose [TypeIdentifier]s will reflect those of remote types. + */ +interface TypeLoader { + /** + * Obtains local types which will have the same [TypeIdentifier]s as the remote types. + * + * @param remoteTypeInformation The type information for the remote types. + */ + fun load(remoteTypeInformation: Collection): Map +} + +/** + * A [TypeLoader] that uses the [ClassCarpenter] to build a class matching the supplied [RemoteTypeInformation] if none + * is visible from the current classloader. + */ +class ClassCarpentingTypeLoader(private val carpenter: RemoteTypeCarpenter, private val classLoader: ClassLoader): TypeLoader { + + val cache = DefaultCacheProvider.createCache() + + override fun load(remoteTypeInformation: Collection): Map { + val remoteInformationByIdentifier = remoteTypeInformation.associateBy { it.typeIdentifier } + + // Grab all the types we can from the cache, or the classloader. + val noCarpentryRequired = remoteInformationByIdentifier.asSequence().mapNotNull { (identifier, _) -> + try { + identifier to cache.computeIfAbsent(identifier) { identifier.getLocalType(classLoader) } + } catch (e: ClassNotFoundException) { + null + } + }.toMap() + + // If we have everything we need, return immediately. + if (noCarpentryRequired.size == remoteTypeInformation.size) return noCarpentryRequired + + // Identify the types which need carpenting up. + val requiringCarpentry = remoteInformationByIdentifier.asSequence().mapNotNull { (identifier, information) -> + if (identifier in noCarpentryRequired) null else information + }.toSet() + + // Build the types requiring carpentry in reverse-dependency order. + // Something else might be trying to carpent these types at the same time as us, so we always consult + // (and populate) the cache. + val carpented = CarpentryDependencyGraph.buildInReverseDependencyOrder(requiringCarpentry) { typeToCarpent -> + cache.computeIfAbsent(typeToCarpent.typeIdentifier) { + carpenter.carpent(typeToCarpent) + } + } + + // Return the complete map of types. + return noCarpentryRequired + carpented + } +} + diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderTests.kt deleted file mode 100644 index b428fdd811..0000000000 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderTests.kt +++ /dev/null @@ -1,382 +0,0 @@ -package net.corda.serialization.internal - -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.whenever -import net.corda.core.contracts.Attachment -import net.corda.core.contracts.Contract -import net.corda.core.crypto.SecureHash -import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.declaredField -import net.corda.core.internal.toWireTransaction -import net.corda.core.node.ServiceHub -import net.corda.core.node.services.AttachmentStorage -import net.corda.core.serialization.* -import net.corda.core.utilities.ByteSequence -import net.corda.core.utilities.OpaqueBytes -import net.corda.node.internal.cordapp.JarScanningCordappLoader -import net.corda.node.internal.cordapp.CordappProviderImpl -import net.corda.nodeapi.DummyContractBackdoor -import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.core.DUMMY_NOTARY_NAME -import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.MockCordappConfigProvider -import net.corda.testing.internal.kryoSpecific -import net.corda.testing.internal.rigorousMock -import net.corda.testing.services.MockAttachmentStorage -import org.apache.commons.io.IOUtils -import org.junit.Assert.* -import org.junit.Rule -import org.junit.Test -import java.io.ByteArrayOutputStream -import java.net.URL -import java.net.URLClassLoader -import java.util.jar.JarOutputStream -import java.util.zip.ZipEntry -import kotlin.test.assertFailsWith - -class AttachmentsClassLoaderTests { - companion object { - val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated.jar") - private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" - private val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party - private val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party - private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext { - val serviceHub = rigorousMock() - doReturn(attachmentStorage).whenever(serviceHub).attachments - return this.withServiceHub(serviceHub) - } - - private fun SerializationContext.withServiceHub(serviceHub: ServiceHub): SerializationContext { - return this.withTokenContext(SerializeAsTokenContextImpl(serviceHub) {}).withProperty(attachmentsClassLoaderEnabledPropertyName, true) - } - } - - @Rule - @JvmField - val testSerialization = SerializationEnvironmentRule() - private val attachments = MockAttachmentStorage() - private val networkParameters = testNetworkParameters() - private val cordappProvider = CordappProviderImpl(JarScanningCordappLoader.fromJarUrls(listOf(ISOLATED_CONTRACTS_JAR_PATH)), MockCordappConfigProvider(), attachments).apply { - start(networkParameters.whitelistedContractImplementations) - } - private val cordapp get() = cordappProvider.cordapps.first() - private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!! - private val appContext get() = cordappProvider.getAppContext(cordapp) - private val serviceHub = rigorousMock().also { - doReturn(attachments).whenever(it).attachments - doReturn(cordappProvider).whenever(it).cordappProvider - doReturn(networkParameters).whenever(it).networkParameters - } - - // These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though - // the class may be on the unit test class path (due to default IDE settings, etc), it won't be loaded into the - // regular app classloader but rather than ClassLoaderForTests. This helps keep our environment clean and - // ensures we have precise control over where it's loaded. - object FilteringClassLoader : ClassLoader() { - @Throws(ClassNotFoundException::class) - override fun loadClass(name: String, resolve: Boolean): Class<*> { - if ("AnotherDummyContract" in name) { - throw ClassNotFoundException(name) - } - return super.loadClass(name, resolve) - } - } - - class ClassLoaderForTests : URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH), FilteringClassLoader) - @Test - fun `dynamically load AnotherDummyContract from isolated contracts jar`() { - ClassLoaderForTests().use { child -> - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as Contract - - assertEquals("helloworld", contract.declaredField("magicString").value) - } - } - - private fun fakeAttachment(filepath: String, content: String): ByteArray { - val bs = ByteArrayOutputStream() - JarOutputStream(bs).use { js -> - js.putNextEntry(ZipEntry(filepath)) - js.writer().apply { append(content); flush() } - js.closeEntry() - } - return bs.toByteArray() - } - - private fun readAttachment(attachment: Attachment, filepath: String): ByteArray { - ByteArrayOutputStream().use { - attachment.extractFile(filepath, it) - return it.toByteArray() - } - } - - @Test - fun `test MockAttachmentStorage open as jar`() { - val storage = attachments - val key = attachmentId - val attachment = storage.openAttachment(key)!! - - val jar = attachment.openAsJAR() - - assertNotNull(jar.nextEntry) - } - - @Test - @Suppress("DEPRECATION") - fun `test overlapping file exception`() { - val storage = attachments - val att0 = attachmentId - val att1 = storage.importAttachment(fakeAttachment("file.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("file.txt", "some other data").inputStream()) - - assertFailsWith(AttachmentsClassLoader.OverlappingAttachments::class) { - AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) - } - } - - @Test - @Suppress("DEPRECATION") - fun basic() { - val storage = attachments - val att0 = attachmentId - val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream()) - - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) - val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) - assertEquals("some data", txt) - } - - @Test - @Suppress("DEPRECATION") - fun `Check platform independent path handling in attachment jars`() { - val storage = MockAttachmentStorage() - - val att1 = storage.importAttachment(fakeAttachment("/folder1/foldera/file1.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("\\folder1\\folderb\\file2.txt", "some other data").inputStream()) - - val data1a = readAttachment(storage.openAttachment(att1)!!, "/folder1/foldera/file1.txt") - assertArrayEquals("some data".toByteArray(), data1a) - - val data1b = readAttachment(storage.openAttachment(att1)!!, "\\folder1\\foldera\\file1.txt") - assertArrayEquals("some data".toByteArray(), data1b) - - val data2a = readAttachment(storage.openAttachment(att2)!!, "\\folder1\\folderb\\file2.txt") - assertArrayEquals("some other data".toByteArray(), data2a) - - val data2b = readAttachment(storage.openAttachment(att2)!!, "/folder1/folderb/file2.txt") - assertArrayEquals("some other data".toByteArray(), data2b) - } - - @Test - @Suppress("DEPRECATION") - fun `loading class AnotherDummyContract`() { - val storage = attachments - val att0 = attachmentId - val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream()) - - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) - val contract = contractClass.newInstance() as Contract - assertEquals(cl, contract.javaClass.classLoader) - assertEquals("helloworld", contract.declaredField("magicString").value) - } - - private fun createContract2Cash(): Contract { - ClassLoaderForTests().use { cl -> - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) - return contractClass.newInstance() as Contract - } - } - - @Test - @Suppress("DEPRECATION") - fun `testing Kryo with ClassLoader (with top level class name)`() { - val contract = createContract2Cash() - - val bytes = contract.serialize() - val storage = attachments - val att0 = attachmentId - val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream()) - - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - - val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(contract.javaClass) - val state2 = bytes.deserialize(context = context) - assertTrue(state2.javaClass.classLoader is AttachmentsClassLoader) - assertNotNull(state2) - } - - // top level wrapper - @CordaSerializable - class Data(val contract: Contract) - - @Test - @Suppress("DEPRECATION") - fun `testing Kryo with ClassLoader (without top level class name)`() { - val data = Data(createContract2Cash()) - - assertNotNull(data.contract) - - val context2 = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(data.contract.javaClass) - - val bytes = data.serialize(context = context2) - val storage = attachments - val att0 = attachmentId - val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream()) - val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream()) - - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - - val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)) - - val state2 = bytes.deserialize(context = context) - assertEquals(cl, state2.contract.javaClass.classLoader) - assertNotNull(state2) - - // We should be able to load same class from a different class loader and have them be distinct. - val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - - val context3 = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl2).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl2)) - - val state3 = bytes.deserialize(context = context3) - assertEquals(cl2, state3.contract.javaClass.classLoader) - assertNotNull(state3) - } - - @Test - fun `test serialization of SecureHash`() { - val secureHash = SecureHash.randomSHA256() - val bytes = secureHash.serialize() - val copiedSecuredHash = bytes.deserialize() - - assertEquals(secureHash, copiedSecuredHash) - } - - @Test - fun `test serialization of OpaqueBytes`() { - val opaqueBytes = OpaqueBytes("0123456789".toByteArray()) - val bytes = opaqueBytes.serialize() - val copiedOpaqueBytes = bytes.deserialize() - - assertEquals(opaqueBytes, copiedOpaqueBytes) - } - - @Test - fun `test serialization of sub-sequence OpaqueBytes`() { - val bytesSequence = ByteSequence.of("0123456789".toByteArray(), 3, 2) - val bytes = bytesSequence.serialize() - val copiedBytesSequence = bytes.deserialize() - - assertEquals(bytesSequence, copiedBytesSequence) - } - - @Test - fun `test serialization of WireTransaction with dynamically loaded contract`() { - val child = appContext.classLoader - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) - val context = SerializationFactory.defaultFactory.defaultContext - .withWhitelisted(contract.javaClass) - .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$State", true, child)) - .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$Commands\$Create", true, child)) - .withServiceHub(serviceHub) - .withClassLoader(child) - - val bytes = run { - val wireTransaction = tx.toWireTransaction(serviceHub, context) - wireTransaction.serialize(context = context) - } - val copiedWireTransaction = bytes.deserialize(context = context) - assertEquals(1, copiedWireTransaction.outputs.size) - // Contracts need to be loaded by the same classloader as the ContractState itself - val contractClassloader = copiedWireTransaction.getOutput(0).javaClass.classLoader - val contract2 = contractClassloader.loadClass(copiedWireTransaction.outputs.first().contract).newInstance() as DummyContractBackdoor - assertEquals(contract2.javaClass.classLoader, copiedWireTransaction.outputs[0].data.javaClass.classLoader) - assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data)) - } - - @Test - fun `test deserialize of WireTransaction where contract cannot be found`() { - kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { - ClassLoaderForTests().use { child -> - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) - val attachmentRef = attachmentId - val bytes = run { - val outboundContext = SerializationFactory.defaultFactory.defaultContext - .withServiceHub(serviceHub) - .withClassLoader(child) - val wireTransaction = tx.toWireTransaction(serviceHub, outboundContext) - wireTransaction.serialize(context = outboundContext) - } - // use empty attachmentStorage - - val e = assertFailsWith(MissingAttachmentsException::class) { - val mockAttStorage = MockAttachmentStorage() - val inboundContext = SerializationFactory.defaultFactory.defaultContext - .withAttachmentStorage(mockAttStorage) - .withAttachmentsClassLoader(listOf(attachmentRef)) - bytes.deserialize(context = inboundContext) - - if (mockAttStorage.openAttachment(attachmentRef) == null) { - throw MissingAttachmentsException(listOf(attachmentRef)) - } - } - assertEquals(attachmentRef, e.ids.single()) - } - } - } - - @Test - fun `test loading a class from attachment during deserialization`() { - ClassLoaderForTests().use { child -> - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child) - val attachmentRef = attachmentId - // We currently ignore annotations in attachments, so manually whitelist. - val inboundContext = SerializationFactory - .defaultFactory - .defaultContext - .withWhitelisted(contract.javaClass) - .withServiceHub(serviceHub) - .withAttachmentsClassLoader(listOf(attachmentRef)) - - // Serialize with custom context to avoid populating the default context with the specially loaded class - val serialized = contract.serialize(context = outboundContext) - // Then deserialize with the attachment class loader associated with the attachment - serialized.deserialize(context = inboundContext) - } - } - - @Test - fun `test loading a class with attachment missing during deserialization`() { - ClassLoaderForTests().use { child -> - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val attachmentRef = SecureHash.randomSHA256() - val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child) - // Serialize with custom context to avoid populating the default context with the specially loaded class - val serialized = contract.serialize(context = outboundContext) - - // Then deserialize with the attachment class loader associated with the attachment - val e = assertFailsWith(MissingAttachmentsException::class) { - // We currently ignore annotations in attachments, so manually whitelist. - val inboundContext = SerializationFactory - .defaultFactory - .defaultContext - .withWhitelisted(contract.javaClass) - .withServiceHub(serviceHub) - .withAttachmentsClassLoader(listOf(attachmentRef)) - serialized.deserialize(context = inboundContext) - } - assertEquals(attachmentRef, e.ids.single()) - } - } -} diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt index 860a04a81c..700b8b0560 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt @@ -14,6 +14,7 @@ import net.corda.core.node.services.AttachmentStorage import net.corda.core.serialization.internal.CheckpointSerializationContext import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.internal.AttachmentsClassLoader import net.corda.node.serialization.kryo.CordaClassResolver import net.corda.node.serialization.kryo.CordaKryo import net.corda.testing.internal.rigorousMock @@ -22,6 +23,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.ExpectedException import java.lang.IllegalStateException +import java.net.URL import java.sql.Connection import java.util.* import kotlin.test.assertEquals @@ -112,6 +114,7 @@ class CordaClassResolverTests { val emptyListClass = listOf().javaClass val emptySetClass = setOf().javaClass val emptyMapClass = mapOf().javaClass + val ISOLATED_CONTRACTS_JAR_PATH: URL = CordaClassResolverTests::class.java.getResource("isolated.jar") } private val emptyWhitelistContext: CheckpointSerializationContext = CheckpointSerializationContextImpl(this.javaClass.classLoader, EmptyWhitelist, emptyMap(), true, null) @@ -201,7 +204,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(DefaultSerializable::class.java) } - private fun importJar(storage: AttachmentStorage, uploader: String = DEPLOYED_CORDAPP_UPLOADER) = AttachmentsClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it, uploader, "") } + private fun importJar(storage: AttachmentStorage, uploader: String = DEPLOYED_CORDAPP_UPLOADER) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it, uploader, "") } @Test(expected = KryoException::class) fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() { diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModelTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModelTests.kt new file mode 100644 index 0000000000..b1bca647c5 --- /dev/null +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPRemoteTypeModelTests.kt @@ -0,0 +1,84 @@ +package net.corda.serialization.internal.amqp + +import net.corda.serialization.internal.amqp.testutils.serializeAndReturnSchema +import net.corda.serialization.internal.amqp.testutils.testDefaultFactory +import net.corda.serialization.internal.model.* +import net.corda.testing.core.SerializationEnvironmentRule +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import java.lang.IllegalArgumentException +import java.util.* + +class AMQPRemoteTypeModelTests { + + @Rule + @JvmField + val serializationEnvRule = SerializationEnvironmentRule() + + private val factory = testDefaultFactory() + private val typeModel = AMQPRemoteTypeModel() + + interface Interface { + val array: Array + val list: List + val map: Map + } + + enum class Enum : Interface { + FOO, BAR, BAZ; + + override val array: Array get() = emptyArray() + override val list: List get() = emptyList() + override val map: Map get() = emptyMap() + } + + open class Superclass(override val array: Array, override val list: List, override val map: Map) + : Interface + + class C(array: Array, list: List, map: Map, val enum: Enum): Superclass(array, list, map) + + class SimpleClass(val a: Int, val b: Double, val c: Short?, val d: ByteArray, val e: ByteArray?) + + @Test + fun `round-trip some types through AMQP serialisations`() { + arrayOf("").assertRemoteType("String[]") + listOf(1).assertRemoteType("List") + arrayOf(listOf(1)).assertRemoteType("List[]") + Enum.BAZ.assertRemoteType("Enum(FOO|BAR|BAZ)") + mapOf("string" to 1).assertRemoteType("Map") + arrayOf(byteArrayOf(1, 2, 3)).assertRemoteType("byte[][]") + + SimpleClass(1, 2.0, null, byteArrayOf(1, 2, 3), byteArrayOf(4, 5, 6)) + .assertRemoteType(""" + SimpleClass + a: int + b: double + c (optional): Short + d: byte[] + e (optional): byte[] + """) + + C(arrayOf("a", "b"), listOf(UUID.randomUUID()), mapOf(UUID.randomUUID() to intArrayOf(1, 2, 3)), Enum.BAZ) + .assertRemoteType(""" + C: Interface + array: String[] + enum: Enum(FOO|BAR|BAZ) + list: List + map: Map + """) + } + + private fun getRemoteType(obj: Any): RemoteTypeInformation { + val output = SerializationOutput(factory) + val schema = output.serializeAndReturnSchema(obj) + val values = typeModel.interpret(SerializationSchemas(schema.schema, schema.transformsSchema)).values + return values.find { it.typeIdentifier.getLocalType().asClass().isAssignableFrom(obj::class.java) } ?: + throw IllegalArgumentException( + "Can't find ${obj::class.java.name} in ${values.map { it.typeIdentifier.name}}") + } + + private fun Any.assertRemoteType(prettyPrinted: String) { + assertEquals(prettyPrinted.trimIndent(), getRemoteType(this).prettyPrint()) + } +} \ No newline at end of file diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt new file mode 100644 index 0000000000..6883a7ac68 --- /dev/null +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt @@ -0,0 +1,197 @@ +package net.corda.serialization.internal.amqp + +import com.google.common.reflect.TypeToken +import net.corda.serialization.internal.model.TypeIdentifier +import org.apache.qpid.proton.amqp.UnsignedShort +import org.junit.Test +import java.io.NotSerializableException +import java.lang.reflect.Type +import java.time.LocalDateTime +import java.util.* +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class AMQPTypeIdentifierParserTests { + + @Test + fun `primitives and arrays`() { + assertParseResult("int") + assertParseResult("int[p]") + assertParseResult>("int[]") + assertParseResult>("int[p][]") + assertParseResult>>("int[][]") + assertParseResult("binary") + assertParseResult>("binary[]") + assertParseResult>("ushort[]") + assertParseResult>>("string[][]") + assertParseResult("uuid") + assertParseResult("timestamp") + + // We set a limit to the depth of arrays-of-arrays-of-arrays... + assertFailsWith { + AMQPTypeIdentifierParser.parse("string" + "[]".repeat(33)) + } + } + + @Test + fun `unparameterised types`() { + assertParseResult("java.time.LocalDateTime") + assertParseResult>("java.time.LocalDateTime[]") + assertParseResult>>("java.time.LocalDateTime[][]") + } + + interface WithParameter { + val value: T + } + + interface WithParameters { + val p: Array + val q: WithParameter> + } + + @Test + fun `parameterised types, nested, with arrays`() { + assertParsesTo>, UUID>>>>>( + "WithParameters[]>>" + ) + + // We set a limit to the maximum depth of nested type parameters. + assertFailsWith { + AMQPTypeIdentifierParser.parse("WithParameter<".repeat(33) + ">".repeat(33)) + } + } + + @Test + fun `compatibility test`() { + assertParsesCompatibly() + assertParsesCompatibly() + assertParsesCompatibly>() + assertParsesCompatibly>() + assertParsesTo>("WithParameter") + assertParsesCompatibly>() + assertParsesCompatibly>>() + assertParsesCompatibly>, UUID>>>>>() + } + + // Old tests for DeserializedParameterizedType + @Test + fun `test nested`() { + verify(" java.util.Map < java.util.Map< java.lang.String, java.lang.Integer >, java.util.Map < java.lang.Long , java.lang.String > >") + } + + @Test + fun `test simple`() { + verify("java.util.List") + } + + @Test + fun `test multiple args`() { + verify("java.util.Map") + } + + @Test + fun `test trailing whitespace`() { + verify("java.util.Map ") + } + + @Test + fun `test list of commands`() { + verify("java.util.List>>") + } + + @Test(expected = NotSerializableException::class) + fun `test trailing text`() { + verify("java.util.Mapfoo") + } + + @Test(expected = NotSerializableException::class) + fun `test trailing comma`() { + verify("java.util.Map") + } + + @Test(expected = NotSerializableException::class) + fun `test leading comma`() { + verify("java.util.Map<,java.lang.String, java.lang.Integer>") + } + + @Test(expected = NotSerializableException::class) + fun `test middle comma`() { + verify("java.util.Map<,java.lang.String,, java.lang.Integer>") + } + + @Test(expected = NotSerializableException::class) + fun `test trailing close`() { + verify("java.util.Map>") + } + + @Test(expected = NotSerializableException::class) + fun `test empty params`() { + verify("java.util.Map<>") + } + + @Test(expected = NotSerializableException::class) + fun `test mid whitespace`() { + verify("java.u til.List") + } + + @Test(expected = NotSerializableException::class) + fun `test mid whitespace2`() { + verify("java.util.List") + } + + @Test(expected = NotSerializableException::class) + fun `test wrong number of parameters`() { + verify("java.util.List") + } + + @Test + fun `test no parameters`() { + verify("java.lang.String") + } + + @Test(expected = NotSerializableException::class) + fun `test parameters on non-generic type`() { + verify("java.lang.String") + } + + @Test(expected = NotSerializableException::class) + fun `test excessive nesting`() { + var nested = "java.lang.Integer" + for (i in 1..AMQPTypeIdentifierParser.MAX_TYPE_PARAM_DEPTH) { + nested = "java.util.List<$nested>" + } + verify(nested) + } + + private inline fun assertParseResult(typeString: String) { + assertEquals(TypeIdentifier.forGenericType(typeOf()), AMQPTypeIdentifierParser.parse(typeString)) + } + + private inline fun typeOf() = object : TypeToken() {}.type + + private inline fun assertParsesCompatibly() = assertParsesCompatibly(typeOf()) + + private fun assertParsesCompatibly(type: Type) { + assertParsesTo(type, TypeIdentifier.forGenericType(type).prettyPrint()) + } + + private inline fun assertParsesTo(expectedIdentifierPrettyPrint: String) { + assertParsesTo(typeOf(), expectedIdentifierPrettyPrint) + } + + private fun assertParsesTo(type: Type, expectedIdentifierPrettyPrint: String) { + val nameForType = AMQPTypeIdentifiers.nameForType(type) + val parsedIdentifier = AMQPTypeIdentifierParser.parse(nameForType) + assertEquals(expectedIdentifierPrettyPrint, parsedIdentifier.prettyPrint()) + } + + + private fun normalise(string: String): String { + return string.replace(" ", "") + } + + private fun verify(typeName: String) { + val type = AMQPTypeIdentifierParser.parse(typeName).getLocalType() + assertEquals(normalise(typeName), normalise(type.typeName)) + } +} \ No newline at end of file diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt new file mode 100644 index 0000000000..a2a2cc280d --- /dev/null +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/model/ClassCarpentingTypeLoaderTests.kt @@ -0,0 +1,112 @@ +package net.corda.serialization.internal.model + +import com.fasterxml.jackson.databind.ObjectMapper +import com.google.common.reflect.TypeToken +import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.asClass +import net.corda.serialization.internal.carpenter.ClassCarpenterImpl +import org.junit.Test +import java.lang.reflect.Type +import kotlin.test.assertEquals + +class ClassCarpentingTypeLoaderTests { + + val carpenter = ClassCarpenterImpl(AllWhitelist) + val remoteTypeCarpenter = SchemaBuildingRemoteTypeCarpenter(carpenter) + val typeLoader = ClassCarpentingTypeLoader(remoteTypeCarpenter, carpenter.classloader) + + @Test + fun `carpent some related classes`() { + val addressInformation = RemoteTypeInformation.Composable( + "address", + typeIdentifierOf("net.corda.test.Address"), + mapOf( + "addressLines" to remoteType>().mandatory, + "postcode" to remoteType().optional + ), emptyList(), emptyList() + ) + + val listOfAddresses = RemoteTypeInformation.Parameterised( + "list
", + TypeIdentifier.Parameterised( + "java.util.List", + null, + listOf(addressInformation.typeIdentifier)), + listOf(addressInformation)) + + val personInformation = RemoteTypeInformation.Composable( + "person", + typeIdentifierOf("net.corda.test.Person"), + mapOf( + "name" to remoteType().mandatory, + "age" to remoteType(TypeIdentifier.forClass(Int::class.javaPrimitiveType!!)).mandatory, + "address" to addressInformation.mandatory, + "previousAddresses" to listOfAddresses.mandatory + ), emptyList(), emptyList()) + + val types = typeLoader.load(listOf(personInformation, addressInformation, listOfAddresses)) + val addressType = types[addressInformation.typeIdentifier]!! + val personType = types[personInformation.typeIdentifier]!! + + val address = addressType.make(arrayOf("23 Acacia Avenue", "Surbiton"), "VB6 5UX") + val previousAddress = addressType.make(arrayOf("99 Penguin Lane", "Doncaster"), "RA8 81T") + + val person = personType.make("Arthur Putey", 42, address, listOf(previousAddress)) + val personJson = ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(person) + assertEquals(""" + { + "name" : "Arthur Putey", + "age" : 42, + "address" : { + "addressLines" : [ "23 Acacia Avenue", "Surbiton" ], + "postcode" : "VB6 5UX" + }, + "previousAddresses" : [ { + "addressLines" : [ "99 Penguin Lane", "Doncaster" ], + "postcode" : "RA8 81T" + } ] + } + """.trimIndent(), personJson) + } + + private fun Type.make(vararg params: Any): Any { + val cls = this.asClass() + val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.javaObjectType }.toTypedArray() + val constructor = cls.constructors.find { it.parameterTypes.zip(paramTypes).all { + (expected, actual) -> expected.isAssignableFrom(actual) + } }!! + return constructor.newInstance(*params) + } + + private fun typeIdentifierOf(typeName: String, vararg parameters: TypeIdentifier) = + if (parameters.isEmpty()) TypeIdentifier.Unparameterised(typeName) + else TypeIdentifier.Parameterised(typeName, null, parameters.toList()) + + private inline fun typeOf(): Type = object : TypeToken() {}.type + private inline fun typeIdentifierOf(): TypeIdentifier = TypeIdentifier.forGenericType(typeOf()) + private inline fun remoteType(): RemoteTypeInformation = remoteType(typeIdentifierOf()) + + private fun remoteType(typeIdentifier: TypeIdentifier): RemoteTypeInformation = + when (typeIdentifier) { + is TypeIdentifier.Unparameterised -> RemoteTypeInformation.Unparameterised(typeIdentifier.prettyPrint(), typeIdentifier) + is TypeIdentifier.Parameterised -> RemoteTypeInformation.Parameterised( + typeIdentifier.prettyPrint(), + typeIdentifier, + typeIdentifier.parameters.map { remoteType(it) }) + is TypeIdentifier.ArrayOf -> RemoteTypeInformation.AnArray( + typeIdentifier.prettyPrint(), + typeIdentifier, + remoteType(typeIdentifier.componentType)) + is TypeIdentifier.Erased -> RemoteTypeInformation.Unparameterised( + typeIdentifier.prettyPrint(), + TypeIdentifier.Unparameterised(typeIdentifier.name)) + is TypeIdentifier.TopType -> RemoteTypeInformation.Top + is TypeIdentifier.UnknownType -> RemoteTypeInformation.Unknown + } + + private val RemoteTypeInformation.optional: RemotePropertyInformation get() = + RemotePropertyInformation(this, false) + + private val RemoteTypeInformation.mandatory: RemotePropertyInformation get() = + RemotePropertyInformation(this, true) +} \ No newline at end of file diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt new file mode 100644 index 0000000000..d14646337f --- /dev/null +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt @@ -0,0 +1,166 @@ +package net.corda.serialization.internal.model + +import com.google.common.reflect.TypeToken +import net.corda.core.serialization.SerializableCalculatedProperty +import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.lang.reflect.Type +import java.time.LocalDateTime +import java.util.* + +class LocalTypeModelTests { + + private val descriptorBasedSerializerRegistry = DefaultDescriptorBasedSerializerRegistry() + private val customSerializerRegistry: CustomSerializerRegistry = CachingCustomSerializerRegistry(descriptorBasedSerializerRegistry) + private val model = ConfigurableLocalTypeModel(WhitelistBasedTypeModelConfiguration(AllWhitelist, customSerializerRegistry)) + + interface CollectionHolder { + val list: List + val map: Map + val array: Array> + } + + open class StringKeyedCollectionHolder(override val list: List, override val map: Map, override val array: Array>) : CollectionHolder + + class StringCollectionHolder(list: List, map: Map, array: Array>) : StringKeyedCollectionHolder(list, map, array) + + @Suppress("unused") + class Nested( + val collectionHolder: StringKeyedCollectionHolder?, + private val intArray: IntArray, + optionalParam: Short?) + + // This can't be treated as a composable type, because the [intArray] parameter is mandatory but we have no readable + // field or property to populate it from. + @Suppress("unused") + class NonComposableNested(val collectionHolder: StringKeyedCollectionHolder?, intArray: IntArray) + + @Test + fun `Primitives and collections`() { + assertInformation>("CollectionHolder") + + assertInformation>(""" + StringKeyedCollectionHolder(list: List, map: Map, array: List[]): CollectionHolder + array: List[] + list: List + map: Map + """) + + assertInformation(""" + StringCollectionHolder(list: List, map: Map, array: List[]): StringKeyedCollectionHolder, CollectionHolder + array: List[] + list: List + map: Map + """) + + assertInformation(""" + Nested(collectionHolder: StringKeyedCollectionHolder?, intArray: int[], optionalParam: Short?) + collectionHolder (optional): StringKeyedCollectionHolder(list: List, map: Map, array: List[]): CollectionHolder + array: List[] + list: List + map: Map + intArray: int[] + """) + + assertInformation("NonComposableNested") + } + + interface SuperSuper { + val a: A + val b: B + } + + interface Super : SuperSuper { + val c: List + } + + abstract class Abstract(override val a: Array, override val b: Double) : Super> + + class Concrete(a: Array, b: Double, override val c: List>, val d: Int) : Abstract(a, b) + + @Test + fun `interfaces and superclasses`() { + assertInformation>("SuperSuper") + assertInformation>("Super: SuperSuper") + assertInformation>(""" + Abstract: Super, SuperSuper + a: LocalDateTime[] + b: Double + """) + assertInformation(""" + Concrete(a: Integer[], b: double, c: List, d: int): Abstract, Super, SuperSuper + a: Integer[] + b: Double + c: List + d: int + """) + } + + interface OldStylePojo { + var a: A? + var b: String + @get:SerializableCalculatedProperty + val c: String + } + + class OldStylePojoImpl : OldStylePojo { + override var a: IntArray? = null + override var b: String = "" + override val c: String = a.toString() + b + } + + @Test + fun `getter setter and calculated properties`() { + assertInformation(""" + OldStylePojoImpl(): OldStylePojo + a (optional): int[] + b: String + c (calculated): String + """) + } + + class AliasingOldStylePojoImpl(override var a: String?, override var b: String, override val c: String): OldStylePojo + + @Test + fun `calculated properties aliased by fields in implementing classes`() { + assertInformation(""" + AliasingOldStylePojoImpl(a: String?, b: String, c: String): OldStylePojo + a (optional): String + b: String + c: String + """) + } + + class TransitivelyNonComposable(val a: String, val b: Exception) + + @Test + fun `non-composable types`() { + val serializerRegistry = object: CustomSerializerRegistry { + override fun register(customSerializer: CustomSerializer) {} + + override fun registerExternal(customSerializer: CorDappCustomSerializer) {} + + override fun findCustomSerializer(clazz: Class<*>, declaredType: Type): AMQPSerializer? = null + } + val modelWithoutOpacity = ConfigurableLocalTypeModel(WhitelistBasedTypeModelConfiguration(AllWhitelist, serializerRegistry) ) + assertTrue(modelWithoutOpacity.inspect(typeOf()) is LocalTypeInformation.NonComposable) + assertTrue(modelWithoutOpacity.inspect(typeOf()) is LocalTypeInformation.NonComposable) + } + + private inline fun assertInformation(expected: String) { + assertEquals(expected.trimIndent(), model.inspect(typeOf()).prettyPrint()) + } + + /** + * Handy for seeing what the inspector/pretty printer actually outputs for a type + */ + @Suppress("unused") + private inline fun printInformation() { + println(model.inspect(typeOf()).prettyPrint()) + } + + private inline fun typeOf(): Type = object : TypeToken() {}.type +} \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 7a335b9968..fa38493fce 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -79,7 +79,7 @@ data class DummyContract(val blank: Any? = null) : Contract { */ @JvmStatic fun move(priors: List>, newOwner: AbstractParty): TransactionBuilder { - require(priors.isNotEmpty()) + require(priors.isNotEmpty()){"States to move to new owner must not be empty"} val priorState = priors[0].state.data val (cmd, state) = priorState.withNewOwner(newOwner) return TransactionBuilder(notary = priors[0].state.notary).withItems( diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index e3e7b0ae6d..52ba72d528 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -33,10 +33,13 @@ import net.corda.nodeapi.internal.persistence.isH2Database import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.serialization.internal.amqp.AMQP_ENABLED import net.corda.testing.internal.stubs.CertificateStoreStubs +import java.io.ByteArrayOutputStream import java.nio.file.Files import java.nio.file.Path import java.security.KeyPair import java.util.* +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry import javax.security.auth.x500.X500Principal @Suppress("unused") @@ -170,7 +173,20 @@ fun configureDatabase(hikariProperties: Properties, cacheFactory: NamedCacheFactory = TestingNamedCacheFactory()): CordaPersistence { val isH2Database = isH2Database(hikariProperties.getProperty("dataSource.url", "")) val schemas = if (isH2Database) NodeSchemaService().internalSchemas() else schemaService.schemaOptions.keys - val persistence = createCordaPersistence(databaseConfig, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous, schemaService, cacheFactory) + val persistence = createCordaPersistence(databaseConfig, wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous, schemaService, cacheFactory, null) persistence.startHikariPool(hikariProperties, databaseConfig, schemas) return persistence +} + +/** + * Convenience method for creating a fake attachment containing a file with some content. + */ +fun fakeAttachment(filePath: String, content: String): ByteArray { + val bs = ByteArrayOutputStream() + JarOutputStream(bs).use { js -> + js.putNextEntry(ZipEntry(filePath)) + js.writer().apply { append(content); flush() } + js.closeEntry() + } + return bs.toByteArray() } \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/MockCordappProvider.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/MockCordappProvider.kt index 9c736b6c55..877a53f934 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/MockCordappProvider.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/MockCordappProvider.kt @@ -38,7 +38,7 @@ class MockCordappProvider( allFlows = emptyList(), jarHash = SecureHash.allOnesHash) if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) && it.second == contractHash }) { - cordappRegistry.add(Pair(cordapp, findOrImportAttachment(listOf(contractClassName), contractClassName.toByteArray(), attachments, contractHash, signers))) + cordappRegistry.add(Pair(cordapp, findOrImportAttachment(listOf(contractClassName), fakeAttachmentCached(contractClassName), attachments, contractHash, signers))) } return cordappRegistry.findLast { contractClassName in it.first.contractClassNames }?.second!! } @@ -56,4 +56,9 @@ class MockCordappProvider( attachments.importContractAttachment(contractClassNames, DEPLOYED_CORDAPP_UPLOADER, data.inputStream(), contractHash, signers) } } + + private val attachmentsCache = mutableMapOf() + private fun fakeAttachmentCached(contractClass: String): ByteArray = attachmentsCache.computeIfAbsent(contractClass) { + fakeAttachment(contractClass, contractClass) + } }