Merge branch 'release/os/4.10' into merge-release/os/4.9-release/os/4.10-2024-10-28-389

This commit is contained in:
Chris Cochrane 2024-10-28 14:42:03 +00:00 committed by GitHub
commit e59ebba66a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
118 changed files with 3710 additions and 513 deletions

View File

@ -157,6 +157,8 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob
public <init>(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String)
public <init>(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List, String, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String, net.corda.core.internal.telemetry.SerializedTelemetry)
public <init>(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List, String, net.corda.core.internal.telemetry.SerializedTelemetry, int, kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.context.InvocationOrigin component1()
@NotNull
@ -171,10 +173,14 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob
public final java.util.List<Object> component6()
@Nullable
public final String component7()
@Nullable
public final net.corda.core.internal.telemetry.SerializedTelemetry component8()
@NotNull
public final net.corda.core.context.InvocationContext copy(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor)
@NotNull
public final net.corda.core.context.InvocationContext copy(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String)
@NotNull
public final net.corda.core.context.InvocationContext copy(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String, net.corda.core.internal.telemetry.SerializedTelemetry)
public boolean equals(Object)
@Nullable
public final net.corda.core.context.Actor getActor()
@ -188,6 +194,8 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob
public final net.corda.core.context.Actor getImpersonatedActor()
@NotNull
public final net.corda.core.context.InvocationOrigin getOrigin()
@Nullable
public final net.corda.core.internal.telemetry.SerializedTelemetry getSerializedTelemetry()
@NotNull
public final net.corda.core.context.Trace getTrace()
public int hashCode()
@ -206,6 +214,8 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob
@NotNull
public static final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String)
@NotNull
public static final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String, net.corda.core.internal.telemetry.SerializedTelemetry)
@NotNull
public static final net.corda.core.context.InvocationContext peer(net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor)
@NotNull
public final java.security.Principal principal()
@ -220,6 +230,8 @@ public final class net.corda.core.context.InvocationContext extends java.lang.Ob
@NotNull
public static final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>)
@NotNull
public static final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, net.corda.core.internal.telemetry.SerializedTelemetry)
@NotNull
public static final net.corda.core.context.InvocationContext scheduled(net.corda.core.contracts.ScheduledStateRef, net.corda.core.context.Trace, net.corda.core.context.Trace)
@NotNull
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)
@ -246,6 +258,8 @@ public static final class net.corda.core.context.InvocationContext$Companion ext
@NotNull
public final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String)
@NotNull
public final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.InvocationOrigin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, String, net.corda.core.internal.telemetry.SerializedTelemetry)
@NotNull
public final net.corda.core.context.InvocationContext peer(net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor)
@NotNull
public final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor)
@ -258,6 +272,8 @@ public static final class net.corda.core.context.InvocationContext$Companion ext
@NotNull
public final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>)
@NotNull
public final net.corda.core.context.InvocationContext rpc(net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Trace, net.corda.core.context.Actor, java.util.List<?>, net.corda.core.internal.telemetry.SerializedTelemetry)
@NotNull
public final net.corda.core.context.InvocationContext scheduled(net.corda.core.contracts.ScheduledStateRef, net.corda.core.context.Trace, net.corda.core.context.Trace)
@NotNull
public final net.corda.core.context.InvocationContext service(String, net.corda.core.identity.CordaX500Name, net.corda.core.context.Trace, net.corda.core.context.Trace)
@ -804,7 +820,16 @@ public final class net.corda.core.contracts.PartyAndReference extends java.lang.
@CordaSerializable
public final class net.corda.core.contracts.PrivacySalt extends net.corda.core.utilities.OpaqueBytes
public <init>()
public <init>(int)
public <init>(byte[])
@NotNull
public static final net.corda.core.contracts.PrivacySalt createFor(String)
public static final net.corda.core.contracts.PrivacySalt$Companion Companion
##
public static final class net.corda.core.contracts.PrivacySalt$Companion extends java.lang.Object
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.contracts.PrivacySalt createFor(String)
##
public final class net.corda.core.contracts.ReferencedStateAndRef extends java.lang.Object
public <init>(net.corda.core.contracts.StateAndRef<? extends T>)
@ -882,6 +907,14 @@ public final class net.corda.core.contracts.SignatureAttachmentConstraint extend
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
@NotNull
public String toString()
public static final net.corda.core.contracts.SignatureAttachmentConstraint$Companion Companion
##
public static final class net.corda.core.contracts.SignatureAttachmentConstraint$Companion extends java.lang.Object implements net.corda.core.internal.utilities.Internable
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.contracts.SignatureAttachmentConstraint create(java.security.PublicKey)
@NotNull
public net.corda.core.internal.utilities.PrivateInterner<net.corda.core.contracts.SignatureAttachmentConstraint> getInterner()
##
public final class net.corda.core.contracts.SourceAndAmount extends java.lang.Object
public <init>(P, net.corda.core.contracts.Amount<T>, Object)
@ -999,6 +1032,8 @@ public final class net.corda.core.contracts.Structures extends java.lang.Object
@NotNull
public static final net.corda.core.crypto.SecureHash hash(net.corda.core.contracts.ContractState)
@NotNull
public static final net.corda.core.crypto.SecureHash hash(net.corda.core.contracts.ContractState, String)
@NotNull
public static final net.corda.core.contracts.Amount<T> withoutIssuer(net.corda.core.contracts.Amount<net.corda.core.contracts.Issued<T>>)
public static final int MAX_ISSUER_REF_SIZE = 512
##
@ -1099,6 +1134,10 @@ public abstract class net.corda.core.contracts.TransactionVerificationException
public final net.corda.core.crypto.SecureHash getTxId()
##
@CordaSerializable
public static final class net.corda.core.contracts.TransactionVerificationException$AttachmentTooBigException extends net.corda.core.contracts.TransactionVerificationException
public <init>(net.corda.core.crypto.SecureHash)
##
@CordaSerializable
public static final class net.corda.core.contracts.TransactionVerificationException$BrokenTransactionException extends net.corda.core.contracts.TransactionVerificationException
public <init>(net.corda.core.crypto.SecureHash, String)
##
@ -1257,6 +1296,10 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept
public <init>(net.corda.core.crypto.SecureHash, String, Throwable)
##
@CordaSerializable
public static final class net.corda.core.contracts.TransactionVerificationException$UnsupportedHashTypeException extends net.corda.core.contracts.TransactionVerificationException
public <init>(net.corda.core.crypto.SecureHash)
##
@CordaSerializable
public static final class net.corda.core.contracts.TransactionVerificationException$UntrustedAttachmentsException extends net.corda.core.CordaException
public <init>(net.corda.core.crypto.SecureHash, java.util.List<? extends net.corda.core.crypto.SecureHash>)
@NotNull
@ -1352,6 +1395,8 @@ public interface net.corda.core.cordapp.Cordapp
@NotNull
public abstract java.util.List<Class<? extends net.corda.core.serialization.SerializeAsToken>> getServices()
public abstract int getTargetPlatformVersion()
@NotNull
public abstract java.util.List<Class<? extends net.corda.core.internal.telemetry.TelemetryComponent>> getTelemetryComponents()
##
@DoNotImplement
public static interface net.corda.core.cordapp.Cordapp$Info
@ -1861,6 +1906,53 @@ public interface net.corda.core.crypto.DigestAlgorithm
public abstract byte[] nonceDigest(byte[])
##
@CordaSerializable
public final class net.corda.core.crypto.DigestService extends java.lang.Object
public <init>(String)
@NotNull
public final String component1()
@NotNull
public final net.corda.core.crypto.SecureHash componentHash(net.corda.core.crypto.SecureHash, net.corda.core.utilities.OpaqueBytes)
@NotNull
public final net.corda.core.crypto.SecureHash componentHash(net.corda.core.utilities.OpaqueBytes, net.corda.core.contracts.PrivacySalt, int, int)
@NotNull
public final net.corda.core.crypto.SecureHash computeNonce(net.corda.core.contracts.PrivacySalt, int, int)
@NotNull
public final net.corda.core.crypto.DigestService copy(String)
public boolean equals(Object)
@NotNull
public final net.corda.core.crypto.SecureHash getAllOnesHash()
public final int getDigestLength()
@NotNull
public final String getHashAlgorithm()
@NotNull
public final net.corda.core.crypto.SecureHash getZeroHash()
@NotNull
public final net.corda.core.crypto.SecureHash hash(String)
@NotNull
public final net.corda.core.crypto.SecureHash hash(byte[])
public int hashCode()
@NotNull
public final net.corda.core.crypto.SecureHash serializedHash(T)
@NotNull
public String toString()
public static final net.corda.core.crypto.DigestService$Companion Companion
##
public static final class net.corda.core.crypto.DigestService$Companion extends java.lang.Object
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.crypto.DigestService getDefault()
@NotNull
public final net.corda.core.crypto.DigestService getSha2_256()
@NotNull
public final net.corda.core.crypto.DigestService getSha2_384()
@NotNull
public final net.corda.core.crypto.DigestService getSha2_512()
##
public final class net.corda.core.crypto.DigestServiceKt extends java.lang.Object
@NotNull
public static final net.corda.core.crypto.SecureHash randomHash(net.corda.core.crypto.DigestService)
##
@CordaSerializable
public class net.corda.core.crypto.DigitalSignature extends net.corda.core.utilities.OpaqueBytes
public <init>(byte[])
##
@ -1888,6 +1980,8 @@ public static final class net.corda.core.crypto.MerkleTree$Companion extends jav
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.crypto.MerkleTree getMerkleTree(java.util.List<? extends net.corda.core.crypto.SecureHash>)
@NotNull
public final net.corda.core.crypto.MerkleTree getMerkleTree(java.util.List<? extends net.corda.core.crypto.SecureHash>, net.corda.core.crypto.DigestService)
##
public static final class net.corda.core.crypto.MerkleTree$Leaf extends net.corda.core.crypto.MerkleTree
public <init>(net.corda.core.crypto.SecureHash)
@ -1997,14 +2091,23 @@ public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$Le
##
@CordaSerializable
public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$Node extends net.corda.core.crypto.PartialMerkleTree$PartialTree
@DeprecatedConstructorForDeserialization
public <init>(net.corda.core.crypto.PartialMerkleTree$PartialTree, net.corda.core.crypto.PartialMerkleTree$PartialTree)
public <init>(net.corda.core.crypto.PartialMerkleTree$PartialTree, net.corda.core.crypto.PartialMerkleTree$PartialTree, String)
public <init>(net.corda.core.crypto.PartialMerkleTree$PartialTree, net.corda.core.crypto.PartialMerkleTree$PartialTree, String, int, kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.crypto.PartialMerkleTree$PartialTree component1()
@NotNull
public final net.corda.core.crypto.PartialMerkleTree$PartialTree component2()
@Nullable
public final String component3()
@NotNull
public final net.corda.core.crypto.PartialMerkleTree$PartialTree$Node copy(net.corda.core.crypto.PartialMerkleTree$PartialTree, net.corda.core.crypto.PartialMerkleTree$PartialTree)
@NotNull
public final net.corda.core.crypto.PartialMerkleTree$PartialTree$Node copy(net.corda.core.crypto.PartialMerkleTree$PartialTree, net.corda.core.crypto.PartialMerkleTree$PartialTree, String)
public boolean equals(Object)
@Nullable
public final String getHashAlgorithm()
@NotNull
public final net.corda.core.crypto.PartialMerkleTree$PartialTree getLeft()
@NotNull
@ -2017,36 +2120,86 @@ public static final class net.corda.core.crypto.PartialMerkleTree$PartialTree$No
public abstract class net.corda.core.crypto.SecureHash extends net.corda.core.utilities.OpaqueBytes
public <init>(byte[], kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public static final net.corda.core.crypto.SecureHash allOnesHashFor(String)
@NotNull
public static final net.corda.core.crypto.SecureHash componentHashAs(String, byte[])
@NotNull
public final net.corda.core.crypto.SecureHash concatenate(net.corda.core.crypto.SecureHash)
@NotNull
public final net.corda.core.crypto.SecureHash concatenateAs(String, net.corda.core.crypto.SecureHash)
@NotNull
public static final net.corda.core.crypto.SecureHash create(String)
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 createSHA256(byte[])
@NotNull
protected net.corda.core.crypto.SecureHash generate(byte[])
@NotNull
public static final net.corda.core.crypto.SecureHash hashAs(String, byte[])
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 hashConcat(net.corda.core.crypto.SecureHash)
@NotNull
public static final net.corda.core.crypto.SecureHash nonceHashAs(String, byte[])
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 parse(String)
@NotNull
public final String prefixChars(int)
@NotNull
public static final net.corda.core.crypto.SecureHash random(String)
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 randomSHA256()
@NotNull
public final net.corda.core.crypto.SecureHash reHash()
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 sha256(String)
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 sha256(byte[])
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 sha256Twice(byte[])
@NotNull
public final String toHexString()
@NotNull
public String toString()
@NotNull
public static final net.corda.core.crypto.SecureHash zeroHashFor(String)
public static final net.corda.core.crypto.SecureHash$Companion Companion
public static final char DELIMITER = ':'
@NotNull
public static final String SHA2_256 = "SHA-256"
@NotNull
public static final String SHA2_384 = "SHA-384"
@NotNull
public static final String SHA2_512 = "SHA-512"
@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
public static final class net.corda.core.crypto.SecureHash$Companion extends java.lang.Object implements net.corda.core.internal.utilities.Internable
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.crypto.SecureHash allOnesHashFor(String)
@NotNull
public final net.corda.core.crypto.SecureHash componentHashAs(String, byte[])
@NotNull
public final net.corda.core.crypto.SecureHash create(String)
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 createSHA256(byte[])
public final int digestLengthFor(String)
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 getAllOnesHash()
@NotNull
public net.corda.core.internal.utilities.PrivateInterner<net.corda.core.crypto.SecureHash> getInterner()
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 getZeroHash()
@NotNull
public final net.corda.core.crypto.SecureHash hashAs(String, byte[])
@NotNull
public final net.corda.core.crypto.SecureHash nonceHashAs(String, byte[])
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 parse(String)
@NotNull
public final net.corda.core.crypto.SecureHash random(String)
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 randomSHA256()
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 sha256(String)
@ -2054,14 +2207,39 @@ public static final class net.corda.core.crypto.SecureHash$Companion extends jav
public final net.corda.core.crypto.SecureHash$SHA256 sha256(byte[])
@NotNull
public final net.corda.core.crypto.SecureHash$SHA256 sha256Twice(byte[])
@NotNull
public final net.corda.core.crypto.SecureHash zeroHashFor(String)
##
@CordaSerializable
public static final class net.corda.core.crypto.SecureHash$HASH extends net.corda.core.crypto.SecureHash
public <init>(String, byte[])
public boolean equals(Object)
@NotNull
protected net.corda.core.crypto.SecureHash generate(byte[])
@NotNull
public final String getAlgorithm()
public int hashCode()
@NotNull
public String toString()
##
@CordaSerializable
public static final class net.corda.core.crypto.SecureHash$SHA256 extends net.corda.core.crypto.SecureHash
public <init>(byte[])
public boolean equals(Object)
@NotNull
protected net.corda.core.crypto.SecureHash generate(byte[])
public int hashCode()
@NotNull
public String toString()
##
public final class net.corda.core.crypto.SecureHashKt extends java.lang.Object
@NotNull
public static final String getAlgorithm(net.corda.core.crypto.SecureHash)
@NotNull
public static final net.corda.core.crypto.SecureHash hashAs(net.corda.core.utilities.OpaqueBytes, String)
@NotNull
public static final net.corda.core.crypto.SecureHash hashAs(byte[], String)
public static final boolean isZero(net.corda.core.utilities.OpaqueBytes)
@NotNull
public static final net.corda.core.crypto.SecureHash$SHA256 sha256(net.corda.core.utilities.OpaqueBytes)
@NotNull
@ -3157,6 +3335,24 @@ public static final class net.corda.core.flows.WithReferencedStatesFlow$Companio
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
##
@CordaSerializable
public final class net.corda.core.flows.WrappedFlowExternalAsyncOperation extends java.lang.Object implements net.corda.core.internal.FlowAsyncOperation
public <init>(net.corda.core.flows.FlowExternalAsyncOperation<R>)
@NotNull
public net.corda.core.concurrent.CordaFuture<R> execute(String)
@NotNull
public final net.corda.core.flows.FlowExternalAsyncOperation<R> getOperation()
##
@CordaSerializable
public final class net.corda.core.flows.WrappedFlowExternalOperation extends java.lang.Object implements net.corda.core.internal.FlowAsyncOperation
public <init>(net.corda.core.internal.ServiceHubCoreInternal, net.corda.core.flows.FlowExternalOperation<R>)
@NotNull
public net.corda.core.concurrent.CordaFuture<R> execute(String)
@NotNull
public final net.corda.core.flows.FlowExternalOperation<R> getOperation()
@NotNull
public final net.corda.core.internal.ServiceHubCoreInternal getServiceHub()
##
@DoNotImplement
@CordaSerializable
public abstract class net.corda.core.identity.AbstractParty extends java.lang.Object implements net.corda.core.flows.Destination
@ -3171,6 +3367,7 @@ public abstract class net.corda.core.identity.AbstractParty extends java.lang.Ob
public abstract net.corda.core.contracts.PartyAndReference ref(net.corda.core.utilities.OpaqueBytes)
@NotNull
public final net.corda.core.contracts.PartyAndReference ref(byte...)
public static final net.corda.core.identity.AbstractParty$Companion Companion
##
@DoNotImplement
@CordaSerializable
@ -3182,6 +3379,12 @@ public final class net.corda.core.identity.AnonymousParty extends net.corda.core
public net.corda.core.contracts.PartyAndReference ref(net.corda.core.utilities.OpaqueBytes)
@NotNull
public String toString()
public static final net.corda.core.identity.AnonymousParty$Companion Companion
##
public static final class net.corda.core.identity.AnonymousParty$Companion extends java.lang.Object
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.identity.AnonymousParty create(java.security.PublicKey)
##
@CordaSerializable
public final class net.corda.core.identity.CordaX500Name extends java.lang.Object
@ -3232,11 +3435,13 @@ public final class net.corda.core.identity.CordaX500Name extends java.lang.Objec
public static final int MAX_LENGTH_ORGANISATION_UNIT = 64
public static final int MAX_LENGTH_STATE = 64
##
public static final class net.corda.core.identity.CordaX500Name$Companion extends java.lang.Object
public static final class net.corda.core.identity.CordaX500Name$Companion extends java.lang.Object implements net.corda.core.internal.utilities.Internable
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.identity.CordaX500Name build(javax.security.auth.x500.X500Principal)
@NotNull
public net.corda.core.internal.utilities.PrivateInterner<net.corda.core.identity.CordaX500Name> getInterner()
@NotNull
public final net.corda.core.identity.CordaX500Name parse(String)
##
public final class net.corda.core.identity.IdentityUtils extends java.lang.Object
@ -3262,6 +3467,8 @@ public final class net.corda.core.identity.Party extends net.corda.core.identity
@NotNull
public final net.corda.core.identity.AnonymousParty anonymise()
@NotNull
public final String description()
@NotNull
public final net.corda.core.identity.CordaX500Name getName()
@NotNull
public net.corda.core.identity.CordaX500Name nameOrNull()
@ -3269,6 +3476,14 @@ public final class net.corda.core.identity.Party extends net.corda.core.identity
public net.corda.core.contracts.PartyAndReference ref(net.corda.core.utilities.OpaqueBytes)
@NotNull
public String toString()
public static final net.corda.core.identity.Party$Companion Companion
##
public static final class net.corda.core.identity.Party$Companion extends java.lang.Object
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final net.corda.core.identity.Party create(java.security.cert.X509Certificate)
@NotNull
public final net.corda.core.identity.Party create(net.corda.core.identity.CordaX500Name, java.security.PublicKey)
##
@CordaSerializable
public final class net.corda.core.identity.PartyAndCertificate extends java.lang.Object
@ -3293,6 +3508,8 @@ public final class net.corda.core.identity.PartyAndCertificate extends java.lang
public String toString()
@NotNull
public final java.security.cert.PKIXCertPathValidatorResult verify(java.security.cert.TrustAnchor)
@NotNull
public final java.security.cert.PKIXCertPathValidatorResult verify(java.util.Set<? extends java.security.cert.TrustAnchor>)
##
@CordaSerializable
public interface net.corda.core.messaging.AllPossibleRecipients extends net.corda.core.messaging.MessageRecipients
@ -3330,6 +3547,8 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes
@NotNull
public abstract java.util.Map<String, Boolean> finishedFlowsWithClientIds()
@NotNull
public abstract java.util.Map<String, Boolean> finishedFlowsWithClientIdsAsAdmin()
@NotNull
public abstract net.corda.core.node.NetworkParameters getNetworkParameters()
@NotNull
public abstract Iterable<String> getVaultTransactionNotes(net.corda.core.crypto.SecureHash)
@ -3374,6 +3593,7 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes
@NotNull
public abstract java.util.List<String> registeredFlows()
public abstract boolean removeClientId(String)
public abstract boolean removeClientIdAsAdmin(String)
public abstract void setFlowsDrainingModeEnabled(boolean)
public abstract void shutdown()
@RPCReturnsObservables
@ -3691,6 +3911,11 @@ public static final class net.corda.core.messaging.StateMachineUpdate$Removed ex
public String toString()
##
@DoNotImplement
public interface net.corda.core.messaging.flows.FlowManagerRPCOps extends net.corda.core.messaging.RPCOps
public abstract void debugCheckpoints()
public abstract void dumpCheckpoints()
##
@DoNotImplement
public interface net.corda.core.node.AppServiceHub extends net.corda.core.node.ServiceHub
@NotNull
public abstract net.corda.core.node.services.vault.CordaTransactionSupport getDatabase()
@ -3756,8 +3981,12 @@ public final class net.corda.core.node.NetworkParameters extends java.lang.Objec
public final java.util.Map<String, java.util.List<net.corda.core.crypto.SecureHash>> getWhitelistedContractImplementations()
public int hashCode()
@NotNull
public final net.corda.core.node.NetworkParameters toImmutable()
@NotNull
public String toString()
##
public final class net.corda.core.node.NetworkParametersKt extends java.lang.Object
##
@CordaSerializable
public final class net.corda.core.node.NodeDiagnosticInfo extends java.lang.Object
public <init>(String, String, int, String, java.util.List<net.corda.core.cordapp.CordappInfo>)
@ -3841,6 +4070,8 @@ public interface net.corda.core.node.ServiceHub extends net.corda.core.node.Serv
@NotNull
public abstract T cordaService(Class<T>)
@NotNull
public abstract T cordaTelemetryComponent(Class<T>)
@NotNull
public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction)
@NotNull
public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
@ -3863,6 +4094,8 @@ public interface net.corda.core.node.ServiceHub extends net.corda.core.node.Serv
@NotNull
public abstract net.corda.core.node.services.NetworkMapCache getNetworkMapCache()
@NotNull
public abstract net.corda.core.node.services.TelemetryService getTelemetryService()
@NotNull
public abstract net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService()
@NotNull
public abstract net.corda.core.node.services.TransactionStorage getValidatedTransactions()
@ -4162,6 +4395,11 @@ public final class net.corda.core.node.services.StatesNotAvailableException exte
@NotNull
public String toString()
##
@DoNotImplement
public interface net.corda.core.node.services.TelemetryService
@Nullable
public abstract T getTelemetryHandle(Class<T>)
##
public final class net.corda.core.node.services.TimeWindowChecker extends java.lang.Object
public <init>()
public <init>(java.time.Clock)
@ -5990,6 +6228,11 @@ public final class net.corda.core.serialization.SerializationAPIKt extends java.
public static final net.corda.core.serialization.SerializedBytes<T> serialize(T, net.corda.core.serialization.SerializationFactory, net.corda.core.serialization.SerializationContext)
@NotNull
public static final net.corda.core.serialization.SerializationContext withWhitelist(net.corda.core.serialization.SerializationContext, java.util.List<? extends Class<?>>)
public static final int AMQP_ENVELOPE_CACHE_INITIAL_CAPACITY = 256
@NotNull
public static final String AMQP_ENVELOPE_CACHE_PROPERTY = "AMQP_ENVELOPE_CACHE"
@NotNull
public static final String DESERIALIZATION_CACHE_PROPERTY = "DESERIALIZATION_CACHE"
##
@DoNotImplement
public interface net.corda.core.serialization.SerializationContext
@ -6030,6 +6273,8 @@ public interface net.corda.core.serialization.SerializationContext
@NotNull
public abstract net.corda.core.serialization.SerializationContext withPreventDataLoss()
@NotNull
public abstract net.corda.core.serialization.SerializationContext withProperties(java.util.Map<Object, ?>)
@NotNull
public abstract net.corda.core.serialization.SerializationContext withProperty(Object, Object)
@NotNull
public abstract net.corda.core.serialization.SerializationContext withWhitelisted(Class<?>)
@ -6090,6 +6335,8 @@ public interface net.corda.core.serialization.SerializationSchemeContext
@NotNull
public abstract ClassLoader getDeserializationClassLoader()
@NotNull
public abstract java.util.Map<Object, Object> getProperties()
@NotNull
public abstract net.corda.core.serialization.ClassWhitelist getWhitelist()
##
public interface net.corda.core.serialization.SerializationToken
@ -6206,15 +6453,23 @@ public final class net.corda.core.transactions.ComponentVisibilityException exte
@DoNotImplement
@CordaSerializable
public final class net.corda.core.transactions.ContractUpgradeFilteredTransaction extends net.corda.core.transactions.CoreTransaction
@DeprecatedConstructorForDeserialization
public <init>(java.util.Map<Integer, net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent>, java.util.Map<Integer, ? extends net.corda.core.crypto.SecureHash>)
public <init>(java.util.Map<Integer, net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent>, java.util.Map<Integer, ? extends net.corda.core.crypto.SecureHash>, net.corda.core.crypto.DigestService)
@NotNull
public final java.util.Map<Integer, net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent> component1()
@NotNull
public final java.util.Map<Integer, net.corda.core.crypto.SecureHash> component2()
@NotNull
public final net.corda.core.crypto.DigestService component3()
@NotNull
public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(java.util.Map<Integer, net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent>, java.util.Map<Integer, ? extends net.corda.core.crypto.SecureHash>)
@NotNull
public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(java.util.Map<Integer, net.corda.core.transactions.ContractUpgradeFilteredTransaction$FilteredComponent>, java.util.Map<Integer, ? extends net.corda.core.crypto.SecureHash>, net.corda.core.crypto.DigestService)
public boolean equals(Object)
@NotNull
public final net.corda.core.crypto.DigestService getDigestService()
@NotNull
public final java.util.Map<Integer, net.corda.core.crypto.SecureHash> getHiddenComponents()
@NotNull
public net.corda.core.crypto.SecureHash getId()
@ -6304,8 +6559,11 @@ public static final class net.corda.core.transactions.ContractUpgradeLedgerTrans
@DoNotImplement
@CordaSerializable
public final class net.corda.core.transactions.ContractUpgradeWireTransaction extends net.corda.core.transactions.CoreTransaction
@DeprecatedConstructorForDeserialization
public <init>(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.contracts.PrivacySalt)
@DeprecatedConstructorForDeserialization
public <init>(java.util.List, net.corda.core.contracts.PrivacySalt, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.contracts.PrivacySalt, net.corda.core.crypto.DigestService)
@NotNull
public final net.corda.core.transactions.ContractUpgradeFilteredTransaction buildFilteredTransaction()
@NotNull
@ -6313,9 +6571,15 @@ public final class net.corda.core.transactions.ContractUpgradeWireTransaction ex
@NotNull
public final net.corda.core.contracts.PrivacySalt component2()
@NotNull
public final net.corda.core.crypto.DigestService component3()
@NotNull
public final net.corda.core.transactions.ContractUpgradeWireTransaction copy(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.contracts.PrivacySalt)
@NotNull
public final net.corda.core.transactions.ContractUpgradeWireTransaction copy(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.contracts.PrivacySalt, net.corda.core.crypto.DigestService)
public boolean equals(Object)
@NotNull
public final net.corda.core.crypto.DigestService getDigestService()
@NotNull
public net.corda.core.crypto.SecureHash getId()
@NotNull
public java.util.List<net.corda.core.contracts.StateRef> getInputs()
@ -6389,7 +6653,9 @@ public final class net.corda.core.transactions.FilteredComponentGroup extends ne
@DoNotImplement
@CordaSerializable
public final class net.corda.core.transactions.FilteredTransaction extends net.corda.core.transactions.TraversableTransaction
@DeprecatedConstructorForDeserialization
public <init>(net.corda.core.crypto.SecureHash, java.util.List<net.corda.core.transactions.FilteredComponentGroup>, java.util.List<? extends net.corda.core.crypto.SecureHash>)
public <init>(net.corda.core.crypto.SecureHash, java.util.List<net.corda.core.transactions.FilteredComponentGroup>, java.util.List<? extends net.corda.core.crypto.SecureHash>, net.corda.core.crypto.DigestService)
@NotNull
public static final net.corda.core.transactions.FilteredTransaction buildFilteredTransaction(net.corda.core.transactions.WireTransaction, java.util.function.Predicate<Object>)
public final void checkAllComponentsVisible(net.corda.core.contracts.ComponentGroupEnum)
@ -6433,6 +6699,7 @@ public abstract class net.corda.core.transactions.FullTransaction extends net.co
public final class net.corda.core.transactions.LedgerTransaction extends net.corda.core.transactions.FullTransaction
public <init>(java.util.List<? extends net.corda.core.contracts.StateAndRef<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.TransactionState<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.CommandWithParties<? extends net.corda.core.contracts.CommandData>>, java.util.List<? extends net.corda.core.contracts.Attachment>, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt)
public <init>(java.util.List<? extends net.corda.core.contracts.StateAndRef<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.TransactionState<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.CommandWithParties<? extends net.corda.core.contracts.CommandData>>, java.util.List<? extends net.corda.core.contracts.Attachment>, 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 <init>(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, java.util.List, java.util.List, java.util.List, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function2, net.corda.core.serialization.internal.AttachmentsClassLoaderCache, net.corda.core.crypto.DigestService, kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final java.util.List<net.corda.core.contracts.Command<T>> commandsOfType(Class<T>)
@NotNull
@ -6491,6 +6758,8 @@ public final class net.corda.core.transactions.LedgerTransaction extends net.cor
@NotNull
public final java.util.List<net.corda.core.contracts.CommandWithParties<net.corda.core.contracts.CommandData>> getCommands()
@NotNull
public final net.corda.core.crypto.DigestService getDigestService()
@NotNull
public net.corda.core.crypto.SecureHash getId()
@NotNull
public final net.corda.core.contracts.ContractState getInput(int)
@ -6629,14 +6898,22 @@ public static final class net.corda.core.transactions.NotaryChangeLedgerTransact
@DoNotImplement
@CordaSerializable
public final class net.corda.core.transactions.NotaryChangeWireTransaction extends net.corda.core.transactions.CoreTransaction
@DeprecatedConstructorForDeserialization
public <init>(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>)
public <init>(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.crypto.DigestService)
public <init>(java.util.List<net.corda.core.contracts.StateRef>, net.corda.core.identity.Party, net.corda.core.identity.Party)
@NotNull
public final java.util.List<net.corda.core.utilities.OpaqueBytes> component1()
@NotNull
public final net.corda.core.crypto.DigestService component2()
@NotNull
public final net.corda.core.transactions.NotaryChangeWireTransaction copy(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>)
@NotNull
public final net.corda.core.transactions.NotaryChangeWireTransaction copy(java.util.List<? extends net.corda.core.utilities.OpaqueBytes>, net.corda.core.crypto.DigestService)
public boolean equals(Object)
@NotNull
public final net.corda.core.crypto.DigestService getDigestService()
@NotNull
public net.corda.core.crypto.SecureHash getId()
@NotNull
public java.util.List<net.corda.core.contracts.StateRef> getInputs()
@ -6851,6 +7128,10 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob
public final net.corda.core.transactions.SignedTransaction toSignedTransaction(net.corda.core.node.services.KeyManagementService, java.security.PublicKey, net.corda.core.crypto.SignatureMetadata, net.corda.core.node.ServicesForResolution)
@NotNull
public final net.corda.core.transactions.WireTransaction toWireTransaction(net.corda.core.node.ServicesForResolution)
@NotNull
public final net.corda.core.transactions.WireTransaction toWireTransaction(net.corda.core.node.ServicesForResolution, int)
@NotNull
public final net.corda.core.transactions.WireTransaction toWireTransaction(net.corda.core.node.ServicesForResolution, int, java.util.Map<Object, ?>)
public final void verify(net.corda.core.node.ServiceHub)
@NotNull
public final net.corda.core.transactions.TransactionBuilder withItems(Object...)
@ -6874,7 +7155,9 @@ public interface net.corda.core.transactions.TransactionWithSignatures extends n
@DoNotImplement
@CordaSerializable
public abstract class net.corda.core.transactions.TraversableTransaction extends net.corda.core.transactions.CoreTransaction
@DeprecatedConstructorForDeserialization
public <init>(java.util.List<? extends net.corda.core.transactions.ComponentGroup>)
public <init>(java.util.List<? extends net.corda.core.transactions.ComponentGroup>, net.corda.core.crypto.DigestService)
@NotNull
public final java.util.List<net.corda.core.crypto.SecureHash> getAttachments()
@NotNull
@ -6884,6 +7167,8 @@ public abstract class net.corda.core.transactions.TraversableTransaction extends
@NotNull
public java.util.List<net.corda.core.transactions.ComponentGroup> getComponentGroups()
@NotNull
public final net.corda.core.crypto.DigestService getDigestService()
@NotNull
public java.util.List<net.corda.core.contracts.StateRef> getInputs()
@Nullable
public net.corda.core.crypto.SecureHash getNetworkParametersHash()
@ -6903,8 +7188,11 @@ public final class net.corda.core.transactions.WireTransaction extends net.corda
public <init>(java.util.List<net.corda.core.contracts.StateRef>, java.util.List<? extends net.corda.core.crypto.SecureHash>, java.util.List<? extends net.corda.core.contracts.TransactionState<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.Command<?>>, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow)
public <init>(java.util.List<net.corda.core.contracts.StateRef>, java.util.List<? extends net.corda.core.crypto.SecureHash>, java.util.List<? extends net.corda.core.contracts.TransactionState<? extends net.corda.core.contracts.ContractState>>, java.util.List<? extends net.corda.core.contracts.Command<?>>, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt)
public <init>(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, int, kotlin.jvm.internal.DefaultConstructorMarker)
@DeprecatedConstructorForDeserialization
public <init>(java.util.List<? extends net.corda.core.transactions.ComponentGroup>, net.corda.core.contracts.PrivacySalt)
@DeprecatedConstructorForDeserialization
public <init>(java.util.List, net.corda.core.contracts.PrivacySalt, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(java.util.List<? extends net.corda.core.transactions.ComponentGroup>, net.corda.core.contracts.PrivacySalt, net.corda.core.crypto.DigestService)
@NotNull
public final net.corda.core.transactions.FilteredTransaction buildFilteredTransaction(java.util.function.Predicate<Object>)
public final void checkSignature(net.corda.core.crypto.TransactionSignature)
@ -7290,6 +7578,12 @@ 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
##
public final class net.corda.core.utilities.ThreadDumpUtilsKt extends java.lang.Object
@NotNull
public static final String asString(management.ThreadInfo, int)
@NotNull
public static final String threadDumpAsString()
##
@CordaSerializable
public abstract class net.corda.core.utilities.Try extends java.lang.Object
public <init>(kotlin.jvm.internal.DefaultConstructorMarker)
@ -7709,6 +8003,8 @@ public final class net.corda.testing.core.TestConstants extends java.lang.Object
@NotNull
public static final net.corda.core.identity.CordaX500Name CHARLIE_NAME
@NotNull
public static final net.corda.core.identity.CordaX500Name DAVE_NAME
@NotNull
public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_A_NAME
@NotNull
public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_B_NAME
@ -7752,6 +8048,7 @@ public static final class net.corda.testing.core.TestIdentity$Companion extends
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
public static final T executeTest(java.time.Duration, kotlin.jvm.functions.Function0<kotlin.Unit>, java.time.Duration, kotlin.jvm.functions.Function0<? extends T>)
@NotNull
public static final net.corda.core.utilities.NetworkHostAndPort freeLocalHostAndPort()
public static final int freePort()
@ -8085,6 +8382,8 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
public <init>(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, java.util.Collection, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, boolean)
public <init>(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, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, boolean, boolean)
public <init>(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, java.util.Collection, java.nio.file.Path, java.util.List, java.util.Map, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean)
public final boolean component1()
@NotNull
@ -8107,6 +8406,7 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
public final boolean component19()
@NotNull
public final java.nio.file.Path component2()
public final boolean component20()
@NotNull
public final net.corda.testing.driver.PortAllocation component3()
@NotNull
@ -8125,6 +8425,8 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
@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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, java.util.Collection<? extends net.corda.testing.node.TestCordapp>, java.nio.file.Path, java.util.List<? extends java.nio.file.Path>, java.util.Map<String, String>, boolean, 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<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set<? extends net.corda.testing.node.TestCordapp>)
public boolean equals(Object)
public final boolean getAllowHibernateToManageAppSchema()
@ -8153,6 +8455,7 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
public final java.util.List<net.corda.testing.node.NotarySpec> getNotarySpecs()
@NotNull
public final net.corda.testing.driver.PortAllocation getPortAllocation()
public final boolean getPremigrateH2Database()
public final boolean getStartNodesInProcess()
@NotNull
public final java.util.Map<String, String> getSystemProperties()
@ -8265,6 +8568,8 @@ public final class net.corda.testing.driver.NodeParameters extends java.lang.Obj
public <init>(net.corda.core.identity.CordaX500Name, java.util.List, net.corda.testing.driver.VerifierType, java.util.Map, Boolean, String, java.util.Collection, java.util.Map, String, net.corda.core.utilities.NetworkHostAndPort, int, kotlin.jvm.internal.DefaultConstructorMarker)
@Nullable
public final net.corda.core.identity.CordaX500Name component1()
@Nullable
public final net.corda.core.utilities.NetworkHostAndPort component10()
@NotNull
public final java.util.List<net.corda.testing.node.User> component2()
@NotNull
@ -8300,6 +8605,8 @@ public final class net.corda.testing.driver.NodeParameters extends java.lang.Obj
public final String getMaximumHeapSize()
@Nullable
public final net.corda.core.identity.CordaX500Name getProvidedName()
@Nullable
public final net.corda.core.utilities.NetworkHostAndPort getRpcAddress()
@NotNull
public final java.util.List<net.corda.testing.node.User> getRpcUsers()
@Nullable
@ -8435,6 +8742,11 @@ public static final class net.corda.testing.node.ClusterSpec$Raft extends net.co
@NotNull
public String toString()
##
public final class net.corda.testing.node.DatabaseSnapshot extends java.lang.Object
public final void copyDatabaseSnapshot(java.nio.file.Path)
public final java.nio.file.Path databaseFilename(java.nio.file.Path)
public static final net.corda.testing.node.DatabaseSnapshot INSTANCE
##
@ThreadSafe
public final class net.corda.testing.node.InMemoryMessagingNetwork extends net.corda.core.serialization.SingletonSerializeAsToken
public <init>(boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, org.apache.activemq.artemis.utils.ReusableLatch, kotlin.jvm.internal.DefaultConstructorMarker)
@ -8767,6 +9079,8 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
@NotNull
public T cordaService(Class<T>)
@NotNull
public T cordaTelemetryComponent(Class<T>)
@NotNull
public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction)
@NotNull
public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
@ -8803,6 +9117,8 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
@NotNull
protected final net.corda.core.node.ServicesForResolution getServicesForResolution()
@NotNull
public net.corda.core.internal.telemetry.TelemetryServiceImpl getTelemetryService()
@NotNull
public net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService()
@NotNull
public net.corda.core.node.services.TransactionStorage getValidatedTransactions()
@ -9072,7 +9388,11 @@ public class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, int, kotlin.jvm.internal.DefaultConstructorMarker)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, boolean)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, boolean, boolean)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, boolean, boolean, boolean)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, boolean, boolean, boolean, boolean)
public <init>(java.time.Duration, int, boolean, java.time.Duration, int, int, java.time.Duration, double, int, int, java.time.Duration, boolean, boolean, boolean, boolean, int, kotlin.jvm.internal.DefaultConstructorMarker)
@NotNull
public final java.time.Duration component1()
@NotNull
@ -9099,6 +9419,8 @@ public class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.
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)
@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, boolean, boolean, boolean, boolean)
public boolean equals(Object)
public int getCacheConcurrencyLevel()
@NotNull
@ -9106,14 +9428,18 @@ public class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.
@NotNull
public java.time.Duration getConnectionRetryInterval()
public double getConnectionRetryIntervalMultiplier()
public boolean getCopyBaggageToTags()
@NotNull
public java.time.Duration getDeduplicationCacheExpiry()
public int getMaxFileSize()
public int getMaxReconnectAttempts()
public int getMinimumServerProtocolVersion()
public int getObservationExecutorPoolSize()
public boolean getOpenTelemetryEnabled()
@NotNull
public java.time.Duration getReapInterval()
public boolean getSimpleLogTelemetryEnabled()
public boolean getSpanStartEndEventsEnabled()
public boolean getTrackRpcCallSites()
public int hashCode()
@NotNull
@ -9134,6 +9460,8 @@ public final class net.corda.client.rpc.CordaRPCConnection extends java.lang.Obj
@NotNull
public net.corda.core.messaging.CordaRPCOps getProxy()
public int getServerProtocolVersion()
@Nullable
public T getTelemetryHandle(Class<T>)
public void notifyServerAndClose()
public static final net.corda.client.rpc.CordaRPCConnection$Companion Companion
##
@ -9168,6 +9496,8 @@ public interface net.corda.client.rpc.RPCConnection extends java.io.Closeable
@NotNull
public abstract I getProxy()
public abstract int getServerProtocolVersion()
@Nullable
public abstract T getTelemetryHandle(Class<T>)
public abstract void notifyServerAndClose()
##
public class net.corda.client.rpc.RPCException extends net.corda.core.CordaRuntimeException

View File

@ -13,13 +13,13 @@
* the branch name of origin branch, it should match the current branch
* and it acts as a fail-safe inside {@code forwardMerger} pipeline
*/
String originBranch = 'release/os/4.9'
String originBranch = 'release/os/4.10'
/**
* the branch name of target branch, it should be the branch with the next version
* after the one in current branch.
*/
String targetBranch = 'release/os/4.10'
String targetBranch = 'release/os/4.11'
/**
* Forward merge any changes between #originBranch and #targetBranch

View File

@ -11,7 +11,6 @@ jobs:
- name: Close
uses: corda/jira-sync-closed-action@master
with:
project: CORDA
jiraBaseUrl: https://r3-cev.atlassian.net
jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
jiraToken: ${{ secrets.JIRA_API_TOKEN }}

View File

@ -14,9 +14,8 @@
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" withSubpackages="false" static="false" />
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
<package name="tornadofx" withSubpackages="false" static="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="tornadofx" alias="false" withSubpackages="false" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />

View File

@ -1,6 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

279
.snyk Normal file → Executable file
View File

@ -8,7 +8,7 @@ ignore:
Guavas Files.createTempDir() is used during integration tests only.
Users of Corda are advised not to use Guavas Files.createTempDir()
when building applications on Corda.
expires: 2023-09-01T11:38:11.478Z
expires: 2023-07-21T11:38:11.478Z
created: 2022-12-29T11:38:11.489Z
SNYK-JAVA-COMH2DATABASE-31685:
- '*':
@ -17,7 +17,7 @@ ignore:
When it comes to DB connectivity parameters, we do not allow changing
them as they are supplied by Corda Node configuration file.
expires: 2023-09-01T11:39:26.763Z
expires: 2023-07-21T11:39:26.763Z
created: 2022-12-29T11:39:26.775Z
SNYK-JAVA-COMH2DATABASE-2331071:
- '*':
@ -26,7 +26,7 @@ ignore:
When it comes to DB connectivity parameters, we do not allow changing
them as they are supplied by Corda Node configuration file.
expires: 2023-09-01T11:41:05.707Z
expires: 2023-07-21T11:41:05.707Z
created: 2022-12-29T11:41:05.723Z
SNYK-JAVA-COMSQUAREUPOKHTTP3-2958044:
- '*':
@ -34,7 +34,7 @@ ignore:
The vulnerability in okhttps error handling is only exploitable in
services that receive and parse HTTP requests. Corda does not receive
HTTP requests and thus is not exposed to this issue.
expires: 2023-09-01T11:42:55.546Z
expires: 2023-07-21T11:42:55.546Z
created: 2022-12-29T11:42:55.556Z
SNYK-JAVA-IONETTY-1042268:
- '*':
@ -47,7 +47,7 @@ ignore:
RPC SSL client connections Artemis is used which calls into netty. The
default value for verifyHost is true for Artemis client connectors so
verification of the host name in netty does occur.
expires: 2023-09-01T11:45:42.976Z
expires: 2023-07-21T11:45:42.976Z
created: 2022-12-29T11:45:42.981Z
SNYK-JAVA-ORGJETBRAINSKOTLIN-2628385:
- '*':
@ -57,7 +57,7 @@ ignore:
time for Corda we do not use Multiplatform Gradle Projects so are not
affected by this vulnerability. In addition as it is a build time
vulnerability released artifacts are not affected.
expires: 2023-09-01T11:52:35.855Z
expires: 2023-07-21T11:52:35.855Z
created: 2022-12-29T11:52:35.870Z
SNYK-JAVA-ORGJETBRAINSKOTLIN-2393744:
- '*':
@ -66,8 +66,35 @@ ignore:
temporary files (via Kotlin functions) with insecure permissions.
Corda does not use any of the vulnerable functions so it not
susceptible to this vulnerability.
expires: 2023-09-01T13:39:03.244Z
expires: 2023-07-21T13:39:03.244Z
created: 2022-12-29T13:39:03.262Z
SNYK-JAVA-ORGYAML-3016888:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-07-21T13:39:49.450Z
created: 2022-12-29T13:39:49.470Z
SNYK-JAVA-ORGYAML-2806360:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson except in the optional shell which we recommend using
standalone. The Corda node itself is not exposed. Corda does however
provide mappings of Corda types to allow CorDapps to use Jackson, and
CorDapps using Jackson should make their own assessment. Liquibase is
used to apply the database migration changes. XML files are used here
to define the changes not YAML and therefore the Corda node itself is
not exposed to this DOS vulnerability.
expires: 2023-07-21T13:40:55.262Z
created: 2022-12-29T13:40:55.279Z
SNYK-JAVA-ORGLIQUIBASE-2419059:
- '*':
reason: >-
@ -81,139 +108,8 @@ ignore:
exploit this vulnerability would need access to the server with the
XML input files, and specifically the access and ability to change JAR
files on the file system that make up the Corda installation.
expires: 2023-09-01T13:42:11.552Z
expires: 2023-07-21T13:42:11.552Z
created: 2022-12-29T13:42:11.570Z
SNYK-JAVA-COMH2DATABASE-2348247:
- '*':
reason: >-
H2 console is not enabled for any of the applications we are running.
When it comes to DB connectivity parameters, we do not allow changing
them as they are supplied by Corda Node configuration file.
expires: 2023-09-01T11:36:39.068Z
created: 2022-12-29T11:36:39.089Z
SNYK-JAVA-COMH2DATABASE-1769238:
- '*':
reason: >-
H2 is not invoked by Corda unless the node deployment configures an H2
database. This is not a supported configuration in Production and so
this vulnerability should be irrelevant except during development on
Corda. Corda itself does not store XML data within the database so
Corda is not susceptible to this vulnerability. If CorDapp developers
store XML data to the database they need to ascertain themselves that
they are not susceptible.
expires: 2023-09-01T11:40:29.871Z
created: 2022-12-29T11:40:29.896Z
SNYK-JAVA-ORGYAML-3152153:
- '*':
reason: >-
There is a transitive dependency on snakeyaml from the third party
components jackson-dataformat-yaml and liquidbase-core. The
jackson-dataformat-yaml component does not use the snakeyaml
databinding layer. For liquidbase we use xml in the changelog files
not yaml. So given this Corda is not susceptible to this
vulnerability.Cordapp authors should exercise their own judgment if
using this library directly in their cordapp.
expires: 2023-09-01T11:35:04.385Z
created: 2023-01-04T11:35:04.414Z
SNYK-JAVA-COMH2DATABASE-3146851:
- '*':
reason: >-
Corda does not make use of the H2 web admin console, so it not
susceptible to this reported vulnerability
expires: 2023-09-01T11:45:11.295Z
created: 2023-01-04T11:45:11.322Z
SNYK-JAVA-ORGBOUNCYCASTLE-2841508:
- '*':
reason: >-
This vulnerability relates to weak key-hash message authentication
code due to an error within the BKS version 1 keystore files. Corda
does not use BKS-V1 for its keystore files so is not susceptible to
this vulnerability.
expires: 2023-09-01T11:32:38.120Z
created: 2022-09-21T11:32:38.125Z
SNYK-JAVA-COMFASTERXMLJACKSONCORE-3038424:
- '*':
reason: >-
Corda does not set the non-default UNWRAP_SINGLE_VALUE_ARRAYS required
for this vulnerability. In addition Corda does not use Jackson for
deserialization except in the optional shell which we recommend using
standalone. The Corda node itself is not exposed. Corda does however
provide mappings of Corda types to allow CorDapps to use Jackson, and
CorDapps using Jackson should make their own assessment. This
vulnerability relates to deeply nested untyped Object or Array values
(3000 levels deep). Only CorDapps with these types at this level of
nesting are potentially susceptible.
expires: 2023-09-01T12:04:40.180Z
created: 2023-02-09T12:04:40.209Z
SNYK-JAVA-COMFASTERXMLJACKSONCORE-3038426:
- '*':
reason: >-
Corda does not set the non-default UNWRAP_SINGLE_VALUE_ARRAYS required
for this vulnerability. In addition Corda does not use Jackson for
deserialization except in the optional shell which we recommend using
standalone. The Corda node itself is not exposed. Corda does however
provide mappings of Corda types to allow CorDapps to use Jackson, and
CorDapps using Jackson should make their own assessment. This
vulnerability relates to deeply nested untyped Object or Array values
(3000 levels deep). Only CorDapps with these types at this level of
nesting are potentially susceptible.
expires: 2023-09-01T12:05:03.931Z
created: 2023-02-09T12:05:03.962Z
SNYK-JAVA-ORGYAML-2806360:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson except in the optional shell which we recommend using
standalone. The Corda node itself is not exposed. Corda does however
provide mappings of Corda types to allow CorDapps to use Jackson, and
CorDapps using Jackson should make their own assessment. Liquibase is
used to apply the database migration changes. XML files are used here
to define the changes not YAML and therefore the Corda node itself is
not exposed to this DOS vulnerability.
expires: 2023-09-01T13:40:55.262Z
created: 2022-09-21T13:40:55.279Z
SNYK-JAVA-ORGYAML-3016891:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-09-01T16:37:28.911Z
created: 2023-02-06T16:37:28.933Z
SNYK-JAVA-ORGYAML-3016888:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-09-01T13:39:49.450Z
created: 2022-09-21T13:39:49.470Z
SNYK-JAVA-ORGYAML-3016889:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-09-01T16:35:13.840Z
created: 2023-02-06T16:35:13.875Z
SNYK-JAVA-ORGYAML-3113851:
- '*':
reason: >-
@ -226,6 +122,107 @@ ignore:
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2024-04-01T00:00:00.000Z
created: 2022-11-29T14:55:03.623Z
expires: 2024-04-30T00:00:00.000Z
created: 2022-12-29T14:55:03.623Z
SNYK-JAVA-COMFASTERXMLJACKSONCORE-3038426:
- '*':
reason: >-
Corda does not use Jackson for deserialization except in the optional
shell which we recommend using standalone. The Corda node itself is
not exposed. Corda does however provide mappings of Corda types to
allow CorDapps to use Jackson, and CorDapps using Jackson should make
their own assessment. This vulnerability relates to deeply nested
untyped Object or Array values (3000 levels deep). Only CorDapps with
these types at this level of nesting are potentially susceptible.
expires: 2023-07-12T16:50:57.921Z
created: 2022-12-29T16:50:57.943Z
SNYK-JAVA-COMFASTERXMLJACKSONCORE-3038424:
- '*':
reason: >-
Corda does not use Jackson for deserialization except in the optional
shell which we recommend using standalone. The Corda node itself is
not exposed. Corda does however provide mappings of Corda types to
allow CorDapps to use Jackson, and CorDapps using Jackson should make
their own assessment. This vulnerability relates to deeply nested
untyped Object or Array values (3000 levels deep). Only CorDapps with
these types at this level of nesting are potentially susceptible.
expires: 2023-07-12T16:52:30.722Z
created: 2022-12-29T16:52:30.747Z
SNYK-JAVA-ORGYAML-3016891:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-07-12T17:00:51.957Z
created: 2022-12-29T17:00:51.970Z
SNYK-JAVA-ORGYAML-3016889:
- '*':
reason: >-
Snakeyaml is being used by Jackson and liquidbase. Corda does not use
Jackson for deserialization except in the optional shell which we
recommend using standalone. The Corda node itself is not exposed.
Corda does however provide mappings of Corda types to allow CorDapps
to use Jackson, and CorDapps using Jackson should make their own
assessment. Liquibase is used to apply the database migration changes.
XML files are used here to define the changes not YAML and therefore
the Corda node itself is not exposed to this deserialisation
vulnerability.
expires: 2023-07-12T17:02:02.538Z
created: 2022-12-29T17:02:02.564Z
SNYK-JAVA-COMH2DATABASE-2348247:
- '*':
reason: >-
H2 console is not enabled for any of the applications we are running.
When it comes to DB connectivity parameters, we do not allow changing
them as they are supplied by Corda Node configuration file.
expires: 2023-07-28T11:36:39.068Z
created: 2022-12-29T11:36:39.089Z
SNYK-JAVA-COMH2DATABASE-1769238:
- '*':
reason: >-
H2 is not invoked by Corda unless the node deployment configures an H2
database. This is not a supported configuration in Production and so
this vulnerability should be irrelevant except during development on
Corda. Corda itself does not store XML data within the database so
Corda is not susceptible to this vulnerability. If CorDapp developers
store XML data to the database they need to ascertain themselves that
they are not susceptible.
expires: 2023-07-28T11:40:29.871Z
created: 2022-12-29T11:40:29.896Z
SNYK-JAVA-ORGYAML-3152153:
- '*':
reason: >-
There is a transitive dependency on snakeyaml from the third party
components jackson-dataformat-yaml and liquidbase-core. The
jackson-dataformat-yaml component does not use the snakeyaml
databinding layer. For liquidbase we use xml in the changelog files
not yaml. So given this Corda is not susceptible to this
vulnerability.Cordapp authors should exercise their own judgment if
using this library directly in their cordapp.
expires: 2023-07-03T11:35:04.385Z
created: 2023-01-04T11:35:04.414Z
SNYK-JAVA-IONETTY-3167773:
- '*':
reason: >-
Corda does not use Netty HTTP (and does not use HTTP in the P2P
protocol) . This is a transitive dependency of Netty comms library,
but it is not used in Corda, which uses a custom binary protocol
secured by mutually authenticated TLS. The vulnerability relating to
HTTP Response splitting is not exposed.
expires: 2023-07-03T11:40:51.456Z
created: 2023-01-04T11:40:51.467Z
SNYK-JAVA-COMH2DATABASE-3146851:
- '*':
reason: >-
Corda does not make use of the H2 web admin console, so it not
susceptible to this reported vulnerability
expires: 2023-07-03T11:45:11.295Z
created: 2023-01-04T11:45:11.322Z
patch: {}

View File

@ -8,6 +8,12 @@
Corda is an open source blockchain project, designed for business from the start. Only Corda allows you to build interoperable blockchain networks that transact in strict privacy. Corda's smart contract technology allows businesses to transact directly, with value.
## Architecture Evolution
The code present in this repository reflects the first version of the implementation of the Corda model for DLT technology. This first architecture version covers Corda versions 1 through 4 and continues to deliver on the promise of DLT for both the open source community and industry as a whole.
However, like all things, Corda must evolve to serve the more stringent needs of today. This is why the second (and current) version of the Corda Architecture can be found [here](https://github.com/corda/corda-runtime-os) and will form the basis of the Corda 5 release.
## Features
* Smart contracts that can be written in Java and other JVM languages
@ -31,7 +37,7 @@ Corda is an open source blockchain project, designed for business from the start
* [Documentation](https://docs.corda.net)
* [Stack Overflow Tag](https://stackoverflow.com/questions/tagged/corda)
* [Slack Channel](https://slack.corda.net/)
* [Twitter](https://twitter.com/cordadlt)
* [Twitter](https://twitter.com/Cordablockchain)
* [Meetups](https://www.meetup.com/pro/corda/)
* [Training Courses](https://www.corda.net/corda-training/)

View File

@ -56,21 +56,19 @@ buildscript {
'org.opentest4j**'
]
// gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default.
// We must configure it manually to use the latest capsule version.
ext.capsule_version = '1.0.3'
ext.asm_version = '7.1'
ext.artemis_version = '2.19.1'
// TODO Upgrade Jackson only when corda is using kotlin 1.3.10
ext.jackson_version = '2.17.2'
ext.jackson_kotlin_version = '2.9.7'
ext.jetty_version = '9.4.56.v20240826'
ext.jersey_version = '2.25'
ext.servlet_version = '4.0.1'
ext.assertj_version = '3.12.2'
ext.slf4j_version = '1.7.30'
ext.log4j_version = '2.17.1'
ext.capsule_version = constants.getProperty("capsuleVersion")
ext.open_telemetry_version = constants.getProperty("openTelemetryVersion")
ext.open_telemetry_sem_conv_version = constants.getProperty("openTelemetrySemConvVersion")
ext.asm_version = constants.getProperty("asmVersion")
ext.artemis_version = constants.getProperty("artemisVersion")
ext.jackson_version = constants.getProperty("jacksonVersion")
ext.jackson_kotlin_version = constants.getProperty("jacksonKotlinVersion")
ext.jetty_version = constants.getProperty("jettyVersion")
ext.jersey_version = constants.getProperty("jerseyVersion")
ext.servlet_version = constants.getProperty("servletVersion")
ext.assertj_version = constants.getProperty("assertjVersion")
ext.slf4j_version = constants.getProperty("slf4JVersion")
ext.log4j_version = constants.getProperty("log4JVersion")
ext.bouncycastle_version = constants.getProperty("bouncycastleVersion")
ext.guava_version = constants.getProperty("guavaVersion")
ext.caffeine_version = constants.getProperty("caffeineVersion")
@ -79,66 +77,61 @@ buildscript {
ext.metrics_new_relic_version = constants.getProperty("metricsNewRelicVersion")
ext.djvm_version = constants.getProperty("djvmVersion")
ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion')
ext.okhttp_version = '3.14.2'
ext.netty_version = '4.1.77.Final'
ext.okhttp_version = constants.getProperty("okhttpVersion")
ext.netty_version = constants.getProperty("nettyVersion")
ext.tcnative_version = constants.getProperty("tcnativeVersion")
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
ext.fileupload_version = '1.4'
ext.kryo_version = '4.0.2'
ext.kryo_serializer_version = '0.43'
// Legacy JUnit 4 version
ext.junit_version = '4.12'
// Need this version to access classpath scanning error handling fix -
// see https://github.com/junit-team/junit5/commit/389de48c2a18c5a93a7203ef424aa47a8a835a74
// Upgrade to 5.5.x when GA release is available.
ext.junit_vintage_version = '5.5.0-RC1'
ext.junit_jupiter_version = '5.5.0-RC1'
ext.junit_platform_version = '1.5.0-RC1'
ext.mockito_version = '2.28.2'
ext.mockito_kotlin_version = '1.6.0'
ext.hamkrest_version = '1.7.0.0'
ext.jopt_simple_version = '5.0.2'
ext.jansi_version = '1.18'
ext.hibernate_version = '5.4.32.Final'
ext.h2_version = '1.4.199' // Update docs if renamed or removed.
ext.rxjava_version = '1.3.8'
ext.dokka_version = '0.10.1'
ext.eddsa_version = '0.3.0'
ext.dependency_checker_version = '5.2.0'
ext.commons_collections_version = '4.3'
ext.beanutils_version = '1.9.4'
ext.fileupload_version = constants.getProperty("fileuploadVersion")
ext.kryo_version = constants.getProperty("kryoVersion")
ext.kryo_serializer_version = constants.getProperty("kryoSerializerVersion")
ext.junit_version = constants.getProperty("junitVersion")
ext.junit_vintage_version = constants.getProperty("junitVintageVersion")
ext.junit_jupiter_version = constants.getProperty("junitJupiterVersion")
ext.junit_platform_version = constants.getProperty("junitPlatformVersion")
ext.mockito_version = constants.getProperty("mockitoVersion")
ext.mockito_kotlin_version = constants.getProperty("mockitoKotlinVersion")
ext.hamkrest_version = constants.getProperty("hamkrestVersion")
ext.jopt_simple_version = constants.getProperty("joptSimpleVersion")
ext.jansi_version = constants.getProperty("jansiVersion")
ext.hibernate_version = constants.getProperty("hibernateVersion")
ext.h2_version = constants.getProperty("h2Version")
ext.rxjava_version = constants.getProperty("rxjavaVersion")
ext.dokka_version = constants.getProperty("dokkaVersion")
ext.eddsa_version = constants.getProperty("eddsaVersion")
ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion")
ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion")
ext.beanutils_version = constants.getProperty("beanutilsVersion")
ext.jsr305_version = constants.getProperty("jsr305Version")
ext.shiro_version = '1.10.0'
ext.shiro_version = constants.getProperty("shiroVersion")
ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion')
ext.hikari_version = '3.3.1'
ext.liquibase_version = '3.6.3'
ext.hikari_version = constants.getProperty("hikariVersion")
ext.liquibase_version = constants.getProperty("liquibaseVersion")
ext.artifactory_contextUrl = 'https://software.r3.com/artifactory'
ext.publicArtifactURL = 'https://download.corda.net/maven'
ext.snake_yaml_version = constants.getProperty('snakeYamlVersion')
ext.docker_compose_rule_version = '1.5.0'
ext.selenium_version = '3.141.59'
ext.ghostdriver_version = '2.1.0'
ext.docker_compose_rule_version = constants.getProperty("dockerComposeRuleVersion")
ext.selenium_version = constants.getProperty("seleniumVersion")
ext.ghostdriver_version = constants.getProperty("ghostdriverVersion")
ext.proguard_version = constants.getProperty('proguardVersion')
ext.jsch_version = '0.1.55'
ext.protonj_version = '0.33.0' // Overide Artemis version
ext.snappy_version = '0.5'
ext.jsch_version = constants.getProperty("jschVersion")
ext.protonj_version = constants.getProperty("protonjVersion")
ext.snappy_version = constants.getProperty("snappyVersion")
ext.class_graph_version = constants.getProperty('classgraphVersion')
ext.jcabi_manifests_version = '1.1'
ext.picocli_version = '3.9.6'
ext.commons_lang_version = '3.9'
ext.commons_io_version = '2.17.0'
ext.controlsfx_version = '8.40.15'
ext.jcabi_manifests_version = constants.getProperty("jcabiManifestsVersion")
ext.picocli_version = constants.getProperty("picocliVersion")
ext.commons_lang_version = constants.getProperty("commonsLangVersion")
ext.commons_io_version = constants.getProperty("commonsIoVersion")
ext.controlsfx_version = constants.getProperty("controlsfxVersion")
ext.detekt_version = constants.getProperty('detektVersion')
ext.docker_java_version = constants.getProperty("dockerJavaVersion")
ext.commons_configuration2_version = "2.10.1"
ext.commons_text_version = "1.10.0"
ext.commons_configuration2_version = constants.getProperty("commonsConfiguration2Version")
ext.commons_text_version = constants.getProperty("commonsTextVersion")
ext.snake_yaml_version = constants.getProperty("snakeYamlVersion")
if (JavaVersion.current().isJava8()) {
ext.fontawesomefx_commons_version = '8.15'
ext.fontawesomefx_fontawesome_version = '4.7.0-5'
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsJava8Version")
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeJava8Version")
} else {
// has been compiled by a more recent version of the Java Runtime (class file version 55.0)
ext.fontawesomefx_commons_version = '11.0'
ext.fontawesomefx_fontawesome_version = '4.7.0-11'
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsVersion")
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion")
}
// Name of the IntelliJ SDK created for the deterministic Java rt.jar.
@ -222,7 +215,8 @@ buildscript {
classpath "com.gradle:common-custom-user-data-gradle-plugin:$customUserDataGradlePlugin"
}
configurations.all {
configurations.classpath {
// FORCE Gradle to use latest SNAPSHOT plugins.
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
}
@ -510,25 +504,48 @@ allprojects {
details.useVersion snake_yaml_version
}
}
}
}
compile {
dependencySubstitution {
// We want to use SLF4J's version of these bindings: jcl-over-slf4j
// Remove any transitive dependency on Apache's version.
exclude group: 'commons-logging', module: 'commons-logging'
substitute module('commons-logging:commons-logging') with module("org.slf4j:jcl-over-slf4j:$slf4j_version")
// Remove any transitive dependency on Logback (e.g. Liquibase 3.6 introduces this dependency)
exclude group: 'ch.qos.logback'
substitute module('ch.qos.logback:logback-classic') with module("org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version")
// Netty-All is an uber-jar which contains every Netty module.
// Exclude it to force us to use the individual Netty modules instead.
exclude group: 'io.netty', module: 'netty-all'
}
runtime {
// We never want isolated.jar on classPath, since we want to test jar being dynamically loaded as an attachment
exclude module: 'isolated'
substitute module('io.netty:netty-all') with module("io.netty:netty-common:$netty_version")
// Force dependencies to use the same version of Guava as Corda.
substitute module('com.google.guava:guava') with module("com.google.guava:guava:$guava_version")
// Effectively delete this unused and unwanted transitive dependency of Artemis.
substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version")
}
}
}
// Select all of the compileClasspath and runtimeClasspath etc configurations,
// but NOT the "classpath" configuration, as that is used by the Gradle plugins.
matching { it.name.endsWith("Classpath") }.configureEach { cfg ->
cfg.resolutionStrategy {
dependencySubstitution {
// Force dependencies to use the same version of Kotlin as Corda.
substitute module('org.jetbrains.kotlin:kotlin-stdlib-jdk8') with module("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib-jdk7') with module("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
}
// FORCE Gradle to use latest SNAPSHOT dependencies.
cacheChangingModulesFor 0, 'seconds'
}
}
}
}
sonarqube {
properties {
property "sonar.projectName", "Corda"
@ -666,6 +683,8 @@ bintrayConfig {
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = [
'corda-opentelemetry',
'corda-opentelemetry-driver',
'corda-jfx',
'corda-mock',
'corda-rpc',

View File

@ -78,6 +78,10 @@ class CordaRPCConnection private constructor(
override fun forceClose() = doCloseLogic { actualConnection.forceClose() }
override fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return actualConnection.getTelemetryHandle(telemetryClass)
}
private inline fun doCloseLogic(close: () -> Unit) {
try {
close.invoke()
@ -170,8 +174,15 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
/**
* The cache expiry of a deduplication watermark per client. Default is 1 day.
*/
open val deduplicationCacheExpiry: Duration = 1.days
open val deduplicationCacheExpiry: Duration = 1.days,
open val openTelemetryEnabled: Boolean = true,
open val simpleLogTelemetryEnabled: Boolean = false,
open val spanStartEndEventsEnabled: Boolean = false,
open val copyBaggageToTags: Boolean = false
) {
companion object {
@ -215,7 +226,49 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
connectionRetryIntervalMultiplier,
maxReconnectAttempts,
maxFileSize,
deduplicationCacheExpiry
deduplicationCacheExpiry,
openTelemetryEnabled,
simpleLogTelemetryEnabled,
spanStartEndEventsEnabled,
copyBaggageToTags
)
}
@Suppress("LongParameterList")
fun copy(
connectionMaxRetryInterval: Duration = this.connectionMaxRetryInterval,
minimumServerProtocolVersion: Int = this.minimumServerProtocolVersion,
trackRpcCallSites: Boolean = this.trackRpcCallSites,
reapInterval: Duration = this.reapInterval,
observationExecutorPoolSize: Int = this.observationExecutorPoolSize,
@Suppress("DEPRECATION")
cacheConcurrencyLevel: Int = this.cacheConcurrencyLevel,
connectionRetryInterval: Duration = this.connectionRetryInterval,
connectionRetryIntervalMultiplier: Double = this.connectionRetryIntervalMultiplier,
maxReconnectAttempts: Int = this.maxReconnectAttempts,
maxFileSize: Int = this.maxFileSize,
deduplicationCacheExpiry: Duration = this.deduplicationCacheExpiry,
openTelemetryEnabled: Boolean = this.openTelemetryEnabled,
simpleLogTelemetryEnabled: Boolean = this.simpleLogTelemetryEnabled,
spanStartEndEventsEnabled: Boolean = this.spanStartEndEventsEnabled,
copyBaggageToTags: Boolean = this.copyBaggageToTags
): CordaRPCClientConfiguration {
return CordaRPCClientConfiguration(
connectionMaxRetryInterval,
minimumServerProtocolVersion,
trackRpcCallSites,
reapInterval,
observationExecutorPoolSize,
cacheConcurrencyLevel,
connectionRetryInterval,
connectionRetryIntervalMultiplier,
maxReconnectAttempts,
maxFileSize,
deduplicationCacheExpiry,
openTelemetryEnabled,
simpleLogTelemetryEnabled,
spanStartEndEventsEnabled,
copyBaggageToTags
)
}
@ -236,6 +289,10 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
if (maxReconnectAttempts != other.maxReconnectAttempts) return false
if (maxFileSize != other.maxFileSize) return false
if (deduplicationCacheExpiry != other.deduplicationCacheExpiry) return false
if (openTelemetryEnabled != other.openTelemetryEnabled) return false
if (simpleLogTelemetryEnabled != other.simpleLogTelemetryEnabled) return false
if (spanStartEndEventsEnabled != other.spanStartEndEventsEnabled) return false
if (copyBaggageToTags != other.copyBaggageToTags) return false
return true
}
@ -253,6 +310,10 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
result = 31 * result + maxReconnectAttempts
result = 31 * result + maxFileSize
result = 31 * result + deduplicationCacheExpiry.hashCode()
result = 31 * result + openTelemetryEnabled.hashCode()
result = 31 * result + simpleLogTelemetryEnabled.hashCode()
result = 31 * result + spanStartEndEventsEnabled.hashCode()
result = 31 * result + copyBaggageToTags.hashCode()
return result
}
@ -265,7 +326,11 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
"cacheConcurrencyLevel=$cacheConcurrencyLevel, connectionRetryInterval=$connectionRetryInterval, " +
"connectionRetryIntervalMultiplier=$connectionRetryIntervalMultiplier, " +
"maxReconnectAttempts=$maxReconnectAttempts, maxFileSize=$maxFileSize, " +
"deduplicationCacheExpiry=$deduplicationCacheExpiry)"
"deduplicationCacheExpiry=$deduplicationCacheExpiry, " +
"openTelemetryEnabled=$openTelemetryEnabled, " +
"simpleLogTelemetryEnabled=$simpleLogTelemetryEnabled, " +
"spanStartEndEventsEnabled=$spanStartEndEventsEnabled, " +
"copyBaggageToTags=$copyBaggageToTags )"
}
// Left in for backwards compatibility with version 3.1

View File

@ -21,6 +21,12 @@ interface RPCConnection<out I : RPCOps> : Closeable {
/** The RPC protocol version reported by the server. */
val serverProtocolVersion: Int
/**
* Returns the configured openTelemetry global. Returns null if opentelemetry has not been configured.
*/
fun <T> getTelemetryHandle(telemetryClass: Class<T>): T?
/**
* Closes this client gracefully by sending a notification to the server, so it can immediately clean up resources.
* If the server is not available this method may block for a short period until it's clear the server is not

View File

@ -22,6 +22,7 @@ import net.corda.nodeapi.internal.RoundRobinConnectionPolicy
import net.corda.nodeapi.internal.config.SslConfiguration
import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import java.lang.reflect.Proxy
import java.util.concurrent.CopyOnWriteArraySet
@ -101,10 +102,15 @@ class RPCClient<I : RPCOps>(
// Without this any type of "send" time failures will not be delivered back to the client
isBlockOnNonDurableSend = true
}
val targetString = "${transport.params[TransportConstants.HOST_PROP_NAME]}:${transport.params[TransportConstants.PORT_PROP_NAME]}"
val rpcClientTelemetry = RPCClientTelemetry("rpcClient-$targetString", rpcConfiguration.openTelemetryEnabled,
rpcConfiguration.simpleLogTelemetryEnabled, rpcConfiguration.spanStartEndEventsEnabled, rpcConfiguration.copyBaggageToTags)
val sessionId = Trace.SessionId.newInstance()
val distributionMux = DistributionMux(listeners, username)
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator,
rpcOpsClass, serializationContext, sessionId, externalTrace, impersonatedActor, targetLegalIdentity, distributionMux)
rpcOpsClass, serializationContext, sessionId, externalTrace, impersonatedActor, targetLegalIdentity, distributionMux,
rpcClientTelemetry)
try {
proxyHandler.start()
val ops: I = uncheckedCast(Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), proxyHandler))
@ -121,6 +127,10 @@ class RPCClient<I : RPCOps>(
override val proxy = ops
override val serverProtocolVersion = serverProtocolVersion
override fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return rpcClientTelemetry.getTelemetryHandle(telemetryClass)
}
private fun close(notify: Boolean) {
if (notify) {
proxyHandler.notifyServerAndClose()
@ -128,6 +138,7 @@ class RPCClient<I : RPCOps>(
proxyHandler.forceClose()
}
serverLocator.close()
rpcClientTelemetry.telemetryService.shutdownTelemetry()
}
override fun notifyServerAndClose() {

View File

@ -20,6 +20,7 @@ import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.telemetry.TelemetryStatusCode
import net.corda.core.internal.times
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
@ -110,6 +111,7 @@ internal class RPCClientProxyHandler(
private val impersonatedActor: Actor?,
private val targetLegalIdentity: CordaX500Name?,
private val notificationDistributionMux: DistributionMux<out RPCOps>,
private val rpcClientTelemetry: RPCClientTelemetry,
private val cacheFactory: NamedCacheFactory = ClientCacheFactory()
) : InvocationHandler {
@ -330,6 +332,8 @@ internal class RPCClientProxyHandler(
val replyId = InvocationId.newInstance()
val methodFqn = produceMethodFullyQualifiedName(method)
callSiteMap?.set(replyId, CallSite(methodFqn))
val telemetryId = rpcClientTelemetry.telemetryService.startSpanForFlow("client-$methodFqn", emptyMap())
try {
val serialisedArguments = (arguments?.toList() ?: emptyList()).serialize(context = serializationContextWithObservableContext)
val request = RPCApi.ClientToServer.RpcRequest(
@ -339,7 +343,8 @@ internal class RPCClientProxyHandler(
replyId,
sessionId,
externalTrace,
impersonatedActor
impersonatedActor,
rpcClientTelemetry.telemetryService.getCurrentTelemetryData()
)
val replyFuture = SettableFuture.create<Any>()
require(rpcReplyMap.put(replyId, replyFuture) == null) {
@ -353,13 +358,18 @@ internal class RPCClientProxyHandler(
sendMessage(request)
return replyFuture.getOrThrow()
} catch (e: RuntimeException) {
rpcClientTelemetry.telemetryService.recordException(telemetryId, e)
rpcClientTelemetry.telemetryService.setStatus(telemetryId, TelemetryStatusCode.ERROR, e.message ?: "RuntimeException occurred")
// Already an unchecked exception, so just rethrow it
throw e
} catch (e: Exception) {
rpcClientTelemetry.telemetryService.recordException(telemetryId, e)
rpcClientTelemetry.telemetryService.setStatus(telemetryId, TelemetryStatusCode.ERROR, e.message ?: "Exception occurred")
// This must be a checked exception, so wrap it
throw RPCException(e.message ?: "", e)
} finally {
callSiteMap?.remove(replyId)
rpcClientTelemetry.telemetryService.endSpanForFlow(telemetryId)
}
}

View File

@ -0,0 +1,42 @@
package net.corda.client.rpc.internal
import net.corda.core.internal.telemetry.OpenTelemetryComponent
import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.utilities.contextLogger
class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Boolean,
val simpleLogTelemetryEnabled: Boolean, val spanStartEndEventsEnabled: Boolean,
val copyBaggageToTags: Boolean) {
companion object {
private val log = contextLogger()
}
val telemetryService = TelemetryServiceImpl()
init {
if (openTelemetryEnabled) {
try {
val openTelemetryComponent = OpenTelemetryComponent(serviceName, spanStartEndEventsEnabled, copyBaggageToTags)
if (openTelemetryComponent.isEnabled()) {
telemetryService.addTelemetryComponent(openTelemetryComponent)
log.debug("OpenTelemetry enabled")
}
}
catch (ex: NoClassDefFoundError) {
// Do nothing api or sdk not available on classpath
log.debug("OpenTelemetry not enabled, api or sdk not found on classpath")
}
}
if (simpleLogTelemetryEnabled) {
val simpleLogTelemetryComponent = SimpleLogTelemetryComponent()
telemetryService.addTelemetryComponent(simpleLogTelemetryComponent)
log.debug("SimpleLogTelemetry enabled")
}
}
fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return telemetryService.getTelemetryHandle(telemetryClass)
}
}

View File

@ -299,6 +299,9 @@ class ReconnectingCordaRPCOps private constructor(
currentRPCConnection?.forceClose()
}
}
override fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return currentRPCConnection?.getTelemetryHandle(telemetryClass)
}
fun isClosed(): Boolean = currentState == CLOSED
}
private class ErrorInterceptingHandler(val reconnectingRPCConnection: ReconnectingRPCConnection) : InvocationHandler {

View File

@ -118,7 +118,7 @@ class StandaloneCordaRPClientTest {
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
it.copyTo(NULL_OUTPUT_STREAM)
SecureHash.SHA256(it.hash().asBytes())
SecureHash.createSHA256(it.hash().asBytes())
}
assertEquals(attachment.sha256, hash)
}
@ -133,7 +133,7 @@ class StandaloneCordaRPClientTest {
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
it.copyTo(NULL_OUTPUT_STREAM)
SecureHash.SHA256(it.hash().asBytes())
SecureHash.createSHA256(it.hash().asBytes())
}
assertEquals(attachment.sha256, hash)
}

View File

@ -0,0 +1,109 @@
package net.corda.client.rpc
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.concurrent.fork
import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.millis
import net.corda.coretesting.internal.testThreadFactory
import net.corda.node.services.rpc.RPCServerConfiguration
import net.corda.testing.node.internal.RPCDriverDSL
import net.corda.testing.node.internal.rpcDriver
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import kotlin.test.assertTrue
@RunWith(Parameterized::class)
class QuickRPCRequestTests : AbstractRPCTest() {
companion object {
// indicate when all RPC threads are busy
val threadBusyLatches = ConcurrentHashMap<Long, CountDownLatch>()
val numRpcThreads = 4
}
interface TestOps : RPCOps {
fun newLatch(numberOfDowns: Int): Long
fun waitLatch(id: Long)
fun downLatch(id: Long)
}
class TestOpsImpl : TestOps {
private val latches = ConcurrentHashMap<Long, CountDownLatch>()
override val protocolVersion = 1000
override fun newLatch(numberOfDowns: Int): Long {
val id = random63BitValue()
val latch = CountDownLatch(numberOfDowns)
latches[id] = latch
return id
}
override fun waitLatch(id: Long) {
threadBusyLatches[id]!!.countDown()
latches[id]!!.await()
}
override fun downLatch(id: Long) {
latches[id]!!.countDown()
}
}
private fun RPCDriverDSL.testProxy(): TestProxy<TestOps> {
return testProxy<TestOps>(
TestOpsImpl(),
clientConfiguration = CordaRPCClientConfiguration.DEFAULT.copy(
reapInterval = 100.millis
),
serverConfiguration = RPCServerConfiguration.DEFAULT.copy(
rpcThreadPoolSize = numRpcThreads
)
)
}
private val pool = Executors.newFixedThreadPool(10, testThreadFactory())
@After
fun shutdown() {
pool.shutdown()
}
@Test(timeout=300_000)
fun `quick RPCs by-pass the standard RPC thread pool`() {
/*
1. Set up a node with N RPC threads
2. Send a call to a blocking RPC on each thread
3. When all RPC threads are blocked, call a quick RPC
4. Check the quick RPC returns, whilst the RPC threads are still blocked
*/
rpcDriver {
val proxy = testProxy()
val numberOfDownsRequired = 1
val id = proxy.ops.newLatch(numberOfDownsRequired)
val newThreadLatch = CountDownLatch(numRpcThreads)
threadBusyLatches[id] = newThreadLatch
// Flood the RPC threads with blocking calls
for (n in 1..numRpcThreads) {
pool.fork {
proxy.ops.waitLatch(id)
}
}
// wait until all the RPC threads are blocked
threadBusyLatches[id]!!.await()
// try a quick RPC - getProtocolVersion() is always quick
val quickResult = proxy.ops.protocolVersion.toString()
// the fact that a result is returned is proof enough that the quick-RPC has by-passed the
// flooded RPC thread pool
assertTrue(quickResult.isNotEmpty())
// The failure condition is that the test times out.
}
}
}

View File

@ -9,4 +9,4 @@ package net.corda.common.logging
* (originally added to source control for ease of use)
*/
internal const val CURRENT_MAJOR_RELEASE = "4.9-SNAPSHOT"
internal const val CURRENT_MAJOR_RELEASE = "4.10-SNAPSHOT"

View File

@ -203,11 +203,14 @@
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
</Logger>
<Logger name="org.apache.activemq.artemis.core.server" level="warn" additivity="false">
<AppenderRef ref="Console-ErrorCode-Selector">
<Filters>
<RegexFilter regex=".*AMQ222061.*" onMatch="DENY" onMismatch="NEUTRAL"/>
<RegexFilter regex=".*AMQ222107.*" onMatch="DENY" onMismatch="NEUTRAL"/>
<RegexFilter regex=".*AMQ222165.*" onMatch="DENY" onMismatch="NEUTRAL"/>
<RegexFilter regex=".*AMQ222166.*" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<AppenderRef ref="Console-ErrorCode-Selector"/>
</AppenderRef>
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
</Logger>
<Logger name="org.apache.activemq.audit" level="error" additivity="false">

View File

@ -1,8 +1,9 @@
# This file is parsed from Python in the docs/source/conf.py file
# because some versions here need to be matched by app authors in
# their own projects. So don't get fancy with syntax!
# Fancy syntax - multi pass ${whatever} replacement
cordaVersion=4.9
cordaVersion=4.10
versionSuffix=SNAPSHOT
gradlePluginsVersion=5.0.12
kotlinVersion=1.2.71
@ -11,7 +12,9 @@ java8MinUpdateVersion=171
# When incrementing platformVersion make sure to update #
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
# ***************************************************************#
platformVersion=11
platformVersion=12
openTelemetryVersion=1.20.1
openTelemetrySemConvVersion=1.20.1-alpha
guavaVersion=28.0-jre
# Quasar version to use with Java 8:
quasarVersion=0.7.15_r3
@ -37,3 +40,70 @@ openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
jolokiaAgentVersion=1.6.1
detektVersion=1.0.1
tcnativeVersion=2.0.48.Final
commonsConfiguration2Version=2.11.0
commonsTextVersion=1.10.0
# ENT-6607 all third party version in here now
# gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default.
# We must configure it manually to use the latest capsule version.
capsuleVersion=1.0.3
asmVersion=7.1
artemisVersion=2.19.1
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10
jacksonVersion=2.17.2
jacksonKotlinVersion=2.9.7
jettyVersion=9.4.56.v20240826
jerseyVersion=2.25
servletVersion=4.0.1
assertjVersion=3.12.2
slf4JVersion=1.7.30
log4JVersion=2.17.1
okhttpVersion=3.14.9
nettyVersion=4.1.77.Final
fileuploadVersion=1.4
kryoVersion=4.0.2
kryoSerializerVersion=0.43
# Legacy JUnit 4 version
junitVersion=4.12
# Need this version to access classpath scanning error handling fix -
# see https://github.com/junit-team/junit5/commit/389de48c2a18c5a93a7203ef424aa47a8a835a74
# Upgrade to 5.5.x when GA release is available.
junitVintageVersion=5.5.0-RC1
junitJupiterVersion=5.5.0-RC1
junitPlatformVersion=1.5.0-RC1
mockitoVersion=2.28.2
mockitoKotlinVersion=1.6.0
hamkrestVersion=1.7.0.0
joptSimpleVersion=5.0.2
jansiVersion=1.18
hibernateVersion=5.4.32.Final
# h2Version - Update docs if renamed or removed.
h2Version=1.4.199
rxjavaVersion=1.3.8
dokkaVersion=0.10.1
eddsaVersion=0.3.0
dependencyCheckerVersion=5.2.0
commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4
shiroVersion=1.10.0
hikariVersion=3.3.1
liquibaseVersion=3.6.3
dockerComposeRuleVersion=1.5.0
seleniumVersion=3.141.59
ghostdriverVersion=2.1.0
jschVersion=0.1.55
# Override Artemis version
protonjVersion=0.33.0
snappyVersion=0.5
jcabiManifestsVersion=1.1
picocliVersion=3.9.6
commonsLangVersion=3.9
commonsIoVersion=2.17.0
controlsfxVersion=8.40.15
# FontAwesomeFX for Java8
fontawesomefxCommonsJava8Version=8.15
fontawesomefxFontawesomeJava8Version=4.7.0-5
# FontAwesomeFX for a more recent version of the Java Runtime (class file version 55.0)
fontawesomefxCommonsVersion=11.0
fontawesomefxFontawesomeVersion=4.7.0-11

View File

@ -1,6 +1,7 @@
import net.corda.gradle.jarfilter.JarFilterTask
import net.corda.gradle.jarfilter.MetaFixerTask
import proguard.gradle.ProGuardTask
import static org.gradle.api.JavaVersion.VERSION_1_8
plugins {
@ -43,6 +44,9 @@ dependencies {
api "com.google.code.findbugs:jsr305:$jsr305_version"
api "org.slf4j:slf4j-api:$slf4j_version"
compileOnly "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
compileOnly project(':opentelemetry')
// These dependencies will become "runtime" scoped in our published POM.
// See publish.dependenciesFrom.defaultScope.
deterministicLibraries "org.bouncycastle:bcprov-jdk18on:$bouncycastle_version"
@ -80,6 +84,7 @@ def patchCore = tasks.register('patchCore', Zip) {
exclude 'net/corda/core/contracts/CordaRotatedKeys.class'
exclude 'net/corda/core/contracts/RotatedKeysKt.class'
exclude 'net/corda/core/contracts/RotatedKeys.class'
exclude 'net/corda/core/internal/utilities/PrivateInterner*.class'
}
reproducibleFileOrder = true

View File

@ -0,0 +1,16 @@
package net.corda.core.internal.utilities
import net.corda.core.KeepForDJVM
@KeepForDJVM
class PrivateInterner<T>(val verifier: IternabilityVerifier<T> = AlwaysInternableVerifier()) {
// DJVM implementation does not intern and does not use Guava
fun <S : T> intern(sample: S): S = sample
@KeepForDJVM
companion object {
@Suppress("UNUSED_PARAMETER")
fun findFor(clazz: Class<*>?): PrivateInterner<Any>? = null
}
}

View File

@ -27,7 +27,8 @@ configurations {
dependencies {
obfuscatorImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compileOnly "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
compileOnly project(':opentelemetry')
testImplementation sourceSets.obfuscator.output
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version"
@ -58,7 +59,7 @@ dependencies {
testCompile "org.assertj:assertj-core:${assertj_version}"
// Guava: Google utilities library.
testCompile "com.google.guava:guava:$guava_version"
compile "com.google.guava:guava:$guava_version"
// For caches rather than guava
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
@ -161,7 +162,8 @@ quasar {
"org.w3c.**",
"org.xml**",
"org.yaml**",
"rx**")
"rx**",
"io.opentelemetry.**")
}
artifacts {

View File

@ -4,6 +4,7 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM
import net.corda.core.contracts.ScheduledStateRef
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.CordaSerializable
import java.security.Principal
@ -25,7 +26,8 @@ data class InvocationContext(
val externalTrace: Trace? = null,
val impersonatedActor: Actor? = null,
val arguments: List<Any?>? = emptyList(), // 'arguments' is nullable so that a - >= 4.6 version - RPC client can be backwards compatible against - < 4.6 version - nodes
val clientId: String? = null
val clientId: String? = null,
val serializedTelemetry: SerializedTelemetry? = null
) {
constructor(
@ -36,6 +38,16 @@ data class InvocationContext(
impersonatedActor: Actor? = null
) : this(origin, trace, actor, externalTrace, impersonatedActor, emptyList())
constructor(
origin: InvocationOrigin,
trace: Trace,
actor: Actor?,
externalTrace: Trace? = null,
impersonatedActor: Actor? = null,
arguments: List<Any?>? = emptyList(),
clientId: String? = null
) : this(origin, trace, actor, externalTrace, impersonatedActor, arguments, clientId, serializedTelemetry = null)
companion object {
/**
* Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp.
@ -51,8 +63,9 @@ data class InvocationContext(
externalTrace: Trace? = null,
impersonatedActor: Actor? = null,
arguments: List<Any?> = emptyList(),
clientId: String? = null
) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor, arguments, clientId)
clientId: String? = null,
serializedTelemetry: SerializedTelemetry? = null
) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor, arguments, clientId, serializedTelemetry)
/**
* Creates an [InvocationContext] with [InvocationOrigin.RPC] origin.
@ -60,13 +73,15 @@ data class InvocationContext(
@DeleteForDJVM
@JvmStatic
@JvmOverloads
@Suppress("LongParameterList")
fun rpc(
actor: Actor,
trace: Trace = Trace.newInstance(),
externalTrace: Trace? = null,
impersonatedActor: Actor? = null,
arguments: List<Any?> = emptyList()
): InvocationContext = newInstance(InvocationOrigin.RPC(actor), trace, actor, externalTrace, impersonatedActor, arguments)
arguments: List<Any?> = emptyList(),
serializedTelemetry: SerializedTelemetry? = null
): InvocationContext = newInstance(InvocationOrigin.RPC(actor), trace, actor, externalTrace, impersonatedActor, arguments, serializedTelemetry = serializedTelemetry)
/**
* Creates an [InvocationContext] with [InvocationOrigin.Peer] origin.
@ -119,6 +134,28 @@ data class InvocationContext(
clientId = clientId
)
}
@Suppress("LongParameterList")
fun copy(
origin: InvocationOrigin = this.origin,
trace: Trace = this.trace,
actor: Actor? = this.actor,
externalTrace: Trace? = this.externalTrace,
impersonatedActor: Actor? = this.impersonatedActor,
arguments: List<Any?>? = this.arguments,
clientId: String? = this.clientId
): InvocationContext {
return copy(
origin = origin,
trace = trace,
actor = actor,
externalTrace = externalTrace,
impersonatedActor = impersonatedActor,
arguments = arguments,
clientId = clientId,
serializedTelemetry = serializedTelemetry
)
}
}
/**

View File

@ -1,5 +1,6 @@
package net.corda.core.contracts
import net.corda.core.CordaInternal
import net.corda.core.DoNotImplement
import net.corda.core.KeepForDJVM
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
@ -7,6 +8,8 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.internal.isUploaderTrusted
import net.corda.core.internal.utilities.Internable
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
@ -119,7 +122,16 @@ data class SignatureAttachmentConstraint(val key: PublicKey) : AttachmentConstra
return if (!key.isFulfilledBy(attachment.signerKeys.map { it })) {
log.warn("Untrusted signing key: expected $key. but contract attachment contains ${attachment.signerKeys}")
false
} else true
}
else true
companion object : Internable<SignatureAttachmentConstraint> {
@CordaInternal
override val interner = PrivateInterner<SignatureAttachmentConstraint>()
/**
* Factory method to be used in preference to the constructor.
*/
fun create(key: PublicKey) = interner.intern(SignatureAttachmentConstraint(key))
}
}

View File

@ -6,6 +6,7 @@ import net.corda.core.cordapp.Cordapp.Info.*
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN_VALUE
import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.CheckpointCustomSerializer
import net.corda.core.serialization.SerializationCustomSerializer
@ -49,6 +50,7 @@ interface Cordapp {
val serviceFlows: List<Class<out FlowLogic<*>>>
val schedulableFlows: List<Class<out FlowLogic<*>>>
val services: List<Class<out SerializeAsToken>>
val telemetryComponents: List<Class<out TelemetryComponent>>
val serializationWhitelists: List<SerializationWhitelist>
val serializationCustomSerializers: List<SerializationCustomSerializer<*, *>>
val checkpointCustomSerializers: List<CheckpointCustomSerializer<*, *>>

View File

@ -3,7 +3,13 @@ package net.corda.core.crypto
import net.corda.core.KeepForDJVM
import net.corda.core.serialization.deserialize
import java.io.ByteArrayOutputStream
import java.security.*
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.PrivateKey
import java.security.Provider
import java.security.PublicKey
import java.security.Signature
import java.security.SignatureException
import java.security.spec.AlgorithmParameterSpec
/**
@ -80,7 +86,7 @@ class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
fun engineVerify(sigBytes: ByteArray): Boolean {
val sig = sigBytes.deserialize<CompositeSignaturesWithKeys>()
return if (verifyKey.isFulfilledBy(sig.sigs.map { it.by })) {
val clearData = SecureHash.SHA256(buffer.toByteArray())
val clearData = SecureHash.createSHA256(buffer.toByteArray())
sig.sigs.all { it.isValid(clearData) }
} else {
false

View File

@ -5,11 +5,12 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM
import net.corda.core.crypto.internal.AliasPrivateKey
import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.crypto.internal.`id-Curve25519ph`
import net.corda.core.crypto.internal.bouncyCastlePQCProvider
import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.crypto.internal.cordaSecurityProvider
import net.corda.core.crypto.internal.`id-Curve25519ph`
import net.corda.core.crypto.internal.providerMap
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
@ -710,7 +711,8 @@ object Crypto {
keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom())
else
keyPairGenerator.initialize(signatureScheme.keySize!!, newSecureRandom())
return keyPairGenerator.generateKeyPair()
val newKeyPair = keyPairGenerator.generateKeyPair()
return KeyPair(internPublicKey(newKeyPair.public), newKeyPair.private)
}
/**
@ -840,7 +842,7 @@ object Crypto {
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
val publicKeyD = BCECPublicKey(privateKey.algorithm, publicKeySpec, BouncyCastleProvider.CONFIGURATION)
return KeyPair(publicKeyD, privateKeyD)
return KeyPair(internPublicKey(publicKeyD), privateKeyD)
}
// Deterministically generate an EdDSA key.
@ -853,7 +855,7 @@ object Crypto {
val bytes = macBytes.copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
val privateKeyD = EdDSAPrivateKeySpec(bytes, params)
val publicKeyD = EdDSAPublicKeySpec(privateKeyD.a, params)
return KeyPair(EdDSAPublicKey(publicKeyD), EdDSAPrivateKey(privateKeyD))
return KeyPair(internPublicKey(EdDSAPublicKey(publicKeyD)), EdDSAPrivateKey(privateKeyD))
}
/**
@ -892,7 +894,7 @@ object Crypto {
val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
val priv = EdDSAPrivateKeySpec(bytes, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
return KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return KeyPair(internPublicKey(EdDSAPublicKey(pub)), EdDSAPrivateKey(priv))
}
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
@ -918,7 +920,7 @@ object Crypto {
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
val pub = BCECPublicKey("EC", publicKeySpec, BouncyCastleProvider.CONFIGURATION)
return KeyPair(pub, priv)
return KeyPair(internPublicKey(pub), priv)
}
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
@ -990,11 +992,14 @@ object Crypto {
}
}
private val interner = PrivateInterner<PublicKey>()
private fun internPublicKey(key: PublicKey): PublicKey = interner.intern(key)
private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey {
return when (key) {
return internPublicKey(when (key) {
is BCEdDSAPublicKey -> EdDSAPublicKey(X509EncodedKeySpec(key.encoded))
else -> key
}
})
}
private fun convertIfBCEdDSAPrivateKey(key: PrivateKey): PrivateKey {
@ -1026,11 +1031,11 @@ object Crypto {
@JvmStatic
fun toSupportedPublicKey(key: PublicKey): PublicKey {
return when (key) {
is BCECPublicKey -> key
is BCRSAPublicKey -> key
is BCSphincs256PublicKey -> key
is EdDSAPublicKey -> key
is CompositeKey -> key
is BCECPublicKey -> internPublicKey(key)
is BCRSAPublicKey -> internPublicKey(key)
is BCSphincs256PublicKey -> internPublicKey(key)
is EdDSAPublicKey -> internPublicKey(key)
is CompositeKey -> internPublicKey(key)
is BCEdDSAPublicKey -> convertIfBCEdDSAPublicKey(key)
else -> decodePublicKey(key.encoded)
}

View File

@ -3,9 +3,12 @@
package net.corda.core.crypto
import io.netty.util.concurrent.FastThreadLocal
import net.corda.core.CordaInternal
import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM
import net.corda.core.crypto.internal.DigestAlgorithmFactory
import net.corda.core.internal.utilities.Internable
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.parseAsHex
@ -66,7 +69,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
}
override fun generate(data: ByteArray): SecureHash {
return HASH(algorithm, digestAs(algorithm, data))
return interner.intern(HASH(algorithm, digestAs(algorithm, data)))
}
}
@ -110,7 +113,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
return if(concatAlgorithm == SHA2_256) {
concatBytes.sha256()
} else {
HASH(concatAlgorithm, digestAs(concatAlgorithm, concatBytes))
interner.intern(HASH(concatAlgorithm, digestAs(concatAlgorithm, concatBytes)))
}
}
@ -121,12 +124,15 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
fun reHash() : SecureHash = hashAs(algorithm, bytes)
// Like static methods in Java, except the 'companion' is a singleton that can have state.
companion object {
companion object : Internable<SecureHash> {
const val SHA2_256 = "SHA-256"
const val SHA2_384 = "SHA-384"
const val SHA2_512 = "SHA-512"
const val DELIMITER = ':'
@CordaInternal
override val interner = PrivateInterner<SecureHash>()
/**
* Converts a SecureHash hash value represented as a {algorithm:}hexadecimal [String] into a [SecureHash].
* @param str An optional algorithm id followed by a delimiter and the sequence of hexadecimal digits that represents a hash value.
@ -149,6 +155,14 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
}
}
@JvmStatic
internal fun deintern(hash: SecureHash): SecureHash {
return when (hash) {
is SHA256 -> SHA256(hash.bytes)
else -> HASH(hash.algorithm, hash.bytes)
}
}
/**
* @param algorithm [MessageDigest] algorithm name, in uppercase.
* @param value Hash value as a hexadecimal string.
@ -157,7 +171,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
val digestLength = digestFor(algorithm).digestLength
val data = value.parseAsHex()
return when (data.size) {
digestLength -> HASH(algorithm, data)
digestLength -> interner.intern(HASH(algorithm, data))
else -> throw IllegalArgumentException("Provided string is ${data.size} bytes not $digestLength bytes in hex: $value")
}
}
@ -171,12 +185,18 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
fun parse(str: String?): SHA256 {
return str?.toUpperCase()?.parseAsHex()?.let {
when (it.size) {
32 -> SHA256(it)
32 -> interner.intern(SHA256(it))
else -> throw IllegalArgumentException("Provided string is ${it.size} bytes not 32 bytes in hex: $str")
}
} ?: throw IllegalArgumentException("Provided string is null")
}
/**
* Factory method for SHA256 to be used in preference to the constructor.
*/
@JvmStatic
fun createSHA256(bytes: ByteArray): SHA256 = interner.intern(SHA256(bytes))
private val messageDigests: ConcurrentMap<String, DigestSupplier> = ConcurrentHashMap()
private fun digestFor(algorithm: String): DigestSupplier {
@ -202,9 +222,9 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
fun hashAs(algorithm: String, bytes: ByteArray): SecureHash {
val hashBytes = digestAs(algorithm, bytes)
return if (algorithm == SHA2_256) {
SHA256(hashBytes)
interner.intern(SHA256(hashBytes))
} else {
HASH(algorithm, hashBytes)
interner.intern(HASH(algorithm, hashBytes))
}
}
@ -222,7 +242,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
} else {
val digest = digestFor(algorithm).get()
val hash = digest.componentDigest(bytes)
HASH(algorithm, hash)
interner.intern(HASH(algorithm, hash))
}
}
@ -240,7 +260,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
} else {
val digest = digestFor(algorithm).get()
val hash = digest.nonceDigest(bytes)
HASH(algorithm, hash)
interner.intern(HASH(algorithm, hash))
}
}
@ -249,7 +269,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
* @param bytes The [ByteArray] to hash.
*/
@JvmStatic
fun sha256(bytes: ByteArray) = SHA256(digestAs(SHA2_256, bytes))
fun sha256(bytes: ByteArray) = interner.intern(SHA256(digestAs(SHA2_256, bytes)))
/**
* Computes the SHA-256 hash of the [ByteArray], and then computes the SHA-256 hash of the hash.
@ -282,7 +302,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
randomSHA256()
} else {
val digest = digestFor(algorithm)
HASH(algorithm, digest.get().digest(secureRandomBytes(digest.digestLength)))
interner.intern(HASH(algorithm, digest.get().digest(secureRandomBytes(digest.digestLength))))
}
}
@ -291,7 +311,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
* This field provides more intuitive access from Java.
*/
@JvmField
val zeroHash: SHA256 = SHA256(ByteArray(32) { 0.toByte() })
val zeroHash: SHA256 = interner.intern(SHA256(ByteArray(32) { 0.toByte() }))
/**
* A SHA-256 hash value consisting of 32 0x00 bytes.
@ -305,7 +325,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
* This field provides more intuitive access from Java.
*/
@JvmField
val allOnesHash: SHA256 = SHA256(ByteArray(32) { 255.toByte() })
val allOnesHash: SHA256 = interner.intern(SHA256(ByteArray(32) { 255.toByte() }))
/**
* A SHA-256 hash value consisting of 32 0xFF bytes.
@ -323,8 +343,8 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
return hashConstants.getOrPut(algorithm) {
val digestLength = digestFor(algorithm).digestLength
HashConstants(
zero = HASH(algorithm, ByteArray(digestLength)),
allOnes = HASH(algorithm, ByteArray(digestLength) { 255.toByte() })
zero = interner.intern(HASH(algorithm, ByteArray(digestLength))),
allOnes = interner.intern(HASH(algorithm, ByteArray(digestLength) { 255.toByte() }))
)
}
}

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.isFulfilledBy
import net.corda.core.identity.Party
import net.corda.core.identity.groupAbstractPartyByWellKnownParty
import net.corda.core.internal.pushToLoggingContext
import net.corda.core.internal.telemetry.telemetryServiceInternal
import net.corda.core.internal.warnOnce
import net.corda.core.node.StatesToRecord
import net.corda.core.node.StatesToRecord.ONLY_RELEVANT
@ -221,6 +222,7 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
@Suspendable
private fun notariseAndRecord(): SignedTransaction {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord", flowLogic = this) {
val notarised = if (needsNotarySignature(transaction)) {
progressTracker.currentStep = NOTARISING
val notarySignatures = subFlow(NotaryFlow.Client(transaction, skipVerification = true))
@ -229,11 +231,14 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
logger.info("No need to notarise this transaction.")
transaction
}
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#notariseAndRecord:recordTransactions", flowLogic = this) {
logger.info("Recording transaction locally.")
serviceHub.recordTransactions(statesToRecord, listOf(notarised))
logger.info("Recorded transaction locally successfully.")
}
return notarised
}
}
private fun needsNotarySignature(stx: SignedTransaction): Boolean {
val wtx = stx.tx

View File

@ -16,6 +16,7 @@ import net.corda.core.internal.ServiceHubCoreInternal
import net.corda.core.internal.WaitForStateConsumption
import net.corda.core.internal.abbreviate
import net.corda.core.internal.checkPayloadIs
import net.corda.core.internal.telemetry.telemetryServiceInternal
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.DataFeed
import net.corda.core.node.NodeInfo
@ -157,7 +158,7 @@ abstract class FlowLogic<out T> {
fun initiateFlow(destination: Destination): FlowSession {
require(destination is Party || destination is AnonymousParty) { "Unsupported destination type ${destination.javaClass.name}" }
return stateMachine.initiateFlow(destination, serviceHub.identityService.wellKnownPartyFromAnonymous(destination as AbstractParty)
?: throw IllegalArgumentException("Could not resolve destination: $destination"))
?: throw IllegalArgumentException("Could not resolve destination: $destination"), serviceHub.telemetryServiceInternal.getCurrentTelemetryData())
}
/**
@ -165,7 +166,7 @@ abstract class FlowLogic<out T> {
* that this function does not communicate in itself, the counter-flow will be kicked off by the first send/receive.
*/
@Suspendable
fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, party)
fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, party, serviceHub.telemetryServiceInternal.getCurrentTelemetryData())
/**
* Specifies the identity, with certificate, to use for this flow. This will be one of the multiple identities that
@ -285,12 +286,14 @@ abstract class FlowLogic<out T> {
@Suspendable
internal fun <R : Any> FlowSession.sendAndReceiveWithRetry(receiveType: Class<R>, payload: Any): UntrustworthyData<R> {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAndReceiveWithRetry", mapOf("destination" to destination.toString())) {
val request = FlowIORequest.SendAndReceive(
sessionToMessage = stateMachine.serialize(mapOf(this to payload)),
shouldRetrySend = true
)
return stateMachine.suspend(request, maySkipCheckpoint = false)[this]!!.checkPayloadIs(receiveType)
}
}
@Suspendable
internal inline fun <reified R : Any> FlowSession.sendAndReceiveWithRetry(payload: Any): UntrustworthyData<R> {
@ -310,6 +313,7 @@ abstract class FlowLogic<out T> {
@Suspendable
@JvmOverloads
open fun receiveAllMap(sessions: Map<FlowSession, Class<out Any>>, maySkipCheckpoint: Boolean = false): Map<FlowSession, UntrustworthyData<Any>> {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#receiveAllMap") {
enforceNoPrimitiveInReceive(sessions.values)
val replies = stateMachine.suspend(
ioRequest = FlowIORequest.Receive(sessions.keys.toNonEmptySet()),
@ -317,6 +321,7 @@ abstract class FlowLogic<out T> {
)
return replies.mapValues { (session, payload) -> payload.checkPayloadIs(sessions[session]!!) }
}
}
/**
* Suspends until a message has been received for each session in the specified [sessions].
@ -332,10 +337,12 @@ abstract class FlowLogic<out T> {
@Suspendable
@JvmOverloads
open fun <R : Any> receiveAll(receiveType: Class<R>, sessions: List<FlowSession>, maySkipCheckpoint: Boolean = false): List<UntrustworthyData<R>> {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#receiveAll") {
enforceNoPrimitiveInReceive(listOf(receiveType))
enforceNoDuplicates(sessions)
return castMapValuesToKnownType(receiveAllMap(associateSessionsToReceiveType(receiveType, sessions)))
}
}
/**
* Queues the given [payload] for sending to the provided [sessions] and continues without suspending.
@ -351,9 +358,11 @@ abstract class FlowLogic<out T> {
@Suspendable
@JvmOverloads
fun sendAll(payload: Any, sessions: Set<FlowSession>, maySkipCheckpoint: Boolean = false) {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAll") {
val sessionToPayload = sessions.map { it to payload }.toMap()
return sendAllMap(sessionToPayload, maySkipCheckpoint)
}
}
/**
* Queues the given payloads for sending to the provided sessions and continues without suspending.
@ -368,11 +377,13 @@ abstract class FlowLogic<out T> {
@Suspendable
@JvmOverloads
fun sendAllMap(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) {
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAllMap") {
val request = FlowIORequest.Send(
sessionToMessage = stateMachine.serialize(payloadsPerSession)
)
stateMachine.suspend(request, maySkipCheckpoint)
}
}
/**
* Closes the provided sessions and performs cleanup of any resources tied to these sessions.

View File

@ -5,7 +5,6 @@ import net.corda.core.CordaInternal
import net.corda.core.DoNotImplement
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party
import net.corda.core.internal.BackpressureAwareTimedFlow
@ -174,8 +173,7 @@ class NotaryFlow {
* same transaction.
*/
private fun generateRequestSignature(): NotarisationRequestSignature {
// TODO: This is not required any more once our AMQP serialization supports turning off object referencing.
val notarisationRequest = NotarisationRequest(stx.inputs.map { it.copy(txhash = SecureHash.create(it.txhash.toString())) }, stx.id)
val notarisationRequest = NotarisationRequest(stx.inputs, stx.id)
return notarisationRequest.generateSignature(serviceHub)
}
}

View File

@ -34,7 +34,9 @@ class NotarisationRequest(statesToConsume: List<StateRef>, val transactionId: Se
private val stateRefComparator = compareBy<StateRef>({ it.txhash }, { it.index })
}
// For compatibility reasons, each SecureHash has to be distinct, even if for same value.
private val _statesToConsumeSorted = statesToConsume.sortedWith(stateRefComparator)
.map { StateRef(SecureHash.deintern(it.txhash), it.index) }
/** States this request specifies to be consumed. Sorted to ensure the serialized form does not get affected by the state order. */
val statesToConsume: List<StateRef> get() = _statesToConsumeSorted // Getter required for AMQP serialization

View File

@ -1,8 +1,12 @@
package net.corda.core.identity
import net.corda.core.CordaInternal
import net.corda.core.DoNotImplement
import net.corda.core.contracts.PartyAndReference
import net.corda.core.flows.Destination
import net.corda.core.internal.utilities.Internable
import net.corda.core.internal.utilities.IternabilityVerifier
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes
import java.security.PublicKey
@ -31,4 +35,15 @@ abstract class AbstractParty(val owningKey: PublicKey): Destination {
* ledger.
*/
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes))
@CordaInternal
companion object : Internable<AbstractParty> {
@CordaInternal
override val interner = PrivateInterner<AbstractParty>(object : IternabilityVerifier<AbstractParty> {
override fun choose(original: AbstractParty, interned: AbstractParty): AbstractParty {
// Because Party does not compare name in equals(), don't intern if there's a clash
return if (original.nameOrNull() != interned.nameOrNull()) original else interned
}
})
}
}

View File

@ -22,4 +22,11 @@ class AnonymousParty(owningKey: PublicKey) : Destination, AbstractParty(owningKe
override fun nameOrNull(): CordaX500Name? = null
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
override fun toString() = "Anonymous(${owningKey.toStringShort()})"
companion object {
/**
* Factory method to be used in preference to the constructor.
*/
fun create(owningKey: PublicKey): AnonymousParty = interner.intern(AnonymousParty(owningKey))
}
}

View File

@ -1,10 +1,13 @@
package net.corda.core.identity
import net.corda.core.CordaInternal
import net.corda.core.KeepForDJVM
import net.corda.core.internal.LegalNameValidator
import net.corda.core.internal.toAttributesMap
import net.corda.core.internal.toX500Name
import net.corda.core.internal.unspecifiedCountry
import net.corda.core.internal.utilities.Internable
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.asn1.x500.style.BCStyle
import java.util.*
@ -68,7 +71,7 @@ data class CordaX500Name(val commonName: String?,
}
}
companion object {
companion object : Internable<CordaX500Name> {
@Deprecated("Not Used")
const val LENGTH_COUNTRY = 2
const val MAX_LENGTH_ORGANISATION = 128
@ -80,6 +83,9 @@ data class CordaX500Name(val commonName: String?,
private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU)
private val countryCodes: Set<String> = setOf(*Locale.getISOCountries(), unspecifiedCountry)
@CordaInternal
override val interner = PrivateInterner<CordaX500Name>()
@JvmStatic
fun build(principal: X500Principal): CordaX500Name {
val attrsMap = principal.toAttributesMap(supportedAttributes)
@ -89,7 +95,7 @@ data class CordaX500Name(val commonName: String?,
val L = requireNotNull(attrsMap[BCStyle.L]) { "Corda X.500 names must include an L attribute" }
val ST = attrsMap[BCStyle.ST]
val C = requireNotNull(attrsMap[BCStyle.C]) { "Corda X.500 names must include an C attribute" }
return CordaX500Name(CN, OU, O, L, ST, C)
return interner.intern(CordaX500Name(CN, OU, O, L, ST, C))
}
@JvmStatic

View File

@ -45,4 +45,16 @@ class Party(val name: CordaX500Name, owningKey: PublicKey) : Destination, Abstra
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
override fun toString() = name.toString()
fun description() = "$name (owningKey = ${owningKey.toStringShort()})"
companion object {
/**
* Factory method to be used in preference to the constructor.
*/
fun create(name: CordaX500Name, owningKey: PublicKey): Party = interner.intern(Party(name, owningKey))
/**
* Factory method to be used in preference to the constructor.
*/
fun create(certificate: X509Certificate): Party = interner.intern(Party(certificate))
}
}

View File

@ -6,7 +6,11 @@ import net.corda.core.internal.uncheckedCast
import net.corda.core.internal.validate
import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey
import java.security.cert.*
import java.security.cert.CertPath
import java.security.cert.CertPathValidatorException
import java.security.cert.PKIXCertPathValidatorResult
import java.security.cert.TrustAnchor
import java.security.cert.X509Certificate
/**
* A full party plus the X.509 certificate and path linking the party back to a trust root. Equality of
@ -29,7 +33,7 @@ class PartyAndCertificate(val certPath: CertPath) {
}
@Transient
val party: Party = Party(certificate)
val party: Party = Party.create(certificate)
val owningKey: PublicKey get() = party.owningKey
val name: CordaX500Name get() = party.name

View File

@ -30,7 +30,7 @@ import java.util.jar.JarInputStream
// When incrementing platformVersion make sure to update PLATFORM_VERSION in constants.properties as well.
const val PLATFORM_VERSION = 11
const val PLATFORM_VERSION = 12
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)

View File

@ -8,6 +8,7 @@ import net.corda.core.context.InvocationContext
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.SerializedBytes
import org.slf4j.Logger
@ -30,7 +31,7 @@ interface FlowStateMachine<FLOWRETURN> : FlowStateMachineHandle<FLOWRETURN> {
fun serialize(payloads: Map<FlowSession, Any>): Map<FlowSession, SerializedBytes<Any>>
@Suspendable
fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession
fun initiateFlow(destination: Destination, wellKnownParty: Party, serializedTelemetry: SerializedTelemetry?): FlowSession
fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>)

View File

@ -77,7 +77,6 @@ import java.util.stream.StreamSupport
import java.util.zip.Deflater
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.collections.LinkedHashSet
import kotlin.math.roundToLong
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
@ -165,7 +164,7 @@ fun InputStream.hash(): SecureHash {
}
md.update(buffer, 0, count)
}
SecureHash.SHA256(md.digest())
SecureHash.createSHA256(md.digest())
}
}

View File

@ -8,6 +8,7 @@ import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.notary.NotaryService
import net.corda.core.internal.toPath
import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.CheckpointCustomSerializer
import net.corda.core.serialization.SerializationCustomSerializer
@ -24,6 +25,7 @@ data class CordappImpl(
override val serviceFlows: List<Class<out FlowLogic<*>>>,
override val schedulableFlows: List<Class<out FlowLogic<*>>>,
override val services: List<Class<out SerializeAsToken>>,
override val telemetryComponents: List<Class<out TelemetryComponent>>,
override val serializationWhitelists: List<SerializationWhitelist>,
override val serializationCustomSerializers: List<SerializationCustomSerializer<*, *>>,
override val checkpointCustomSerializers: List<CheckpointCustomSerializer<*, *>>,
@ -79,6 +81,7 @@ data class CordappImpl(
serviceFlows = emptyList(),
schedulableFlows = emptyList(),
services = emptyList(),
telemetryComponents = emptyList(),
serializationWhitelists = emptyList(),
serializationCustomSerializers = emptyList(),
checkpointCustomSerializers = emptyList(),

View File

@ -10,6 +10,7 @@ import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.internal.PlatformVersionSwitches
import net.corda.core.internal.checkParameterHash
import net.corda.core.internal.telemetry.telemetryServiceInternal
import net.corda.core.utilities.seconds
import net.corda.core.utilities.unwrap
import java.lang.IllegalStateException
@ -74,7 +75,7 @@ abstract class NotaryServiceFlow(
sleep(Duration.ZERO)
}
}
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#call:commitInputStates", flowLogic = this) {
service.commitInputStates(
tx.inputs,
tx.id,
@ -82,6 +83,7 @@ abstract class NotaryServiceFlow(
requestPayload.requestSignature,
tx.timeWindow,
tx.references)
}
} catch (e: NotaryInternalException) {
logError(e.error)
// Any exception that's not a NotaryInternalException is assumed to be an unexpected internal error

View File

@ -0,0 +1,374 @@
package net.corda.core.internal.telemetry
import io.opentelemetry.api.GlobalOpenTelemetry
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.baggage.Baggage
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.common.AttributesBuilder
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.context.Context
import io.opentelemetry.context.Scope
import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import net.corda.opentelemetrydriver.OpenTelemetryDriver
import io.opentelemetry.context.propagation.TextMapGetter
import java.util.concurrent.ConcurrentLinkedDeque
@CordaSerializable
data class SpanEventContexts(val child: Context, val parent: Context)
@CordaSerializable
data class ContextCarrier(val context: MutableMap<String,String>)
@CordaSerializable
data class OpenTelemetryContext(val context: ContextCarrier, val spanEventChildContext: ContextCarrier, val spanEventParentContext: ContextCarrier, val baggage: Map<String,String>): TelemetryDataItem
data class SpanInfo(val name: String, val span: Span, val spanScope: Scope,
val spanEventContext: SpanEventContexts? = null,
val spanEventContextQueue: ConcurrentLinkedDeque<SpanEventContexts>? = null)
class TracerSetup(serviceName: String) {
private var openTelemetryDriver: Any? = null
val openTelemetry: OpenTelemetry by lazy {
try {
openTelemetryDriver = OpenTelemetryDriver(serviceName)
(openTelemetryDriver as OpenTelemetryDriver).openTelemetry
}
catch (ex: NoClassDefFoundError) {
GlobalOpenTelemetry.get()
}
}
fun getTracer(): Tracer {
return openTelemetry.tracerProvider.get(OpenTelemetryComponent::class.java.name)
}
fun shutdown() {
(openTelemetryDriver as? OpenTelemetryDriver)?.shutdown()
}
}
@Suppress("TooManyFunctions")
class OpenTelemetryComponent(val serviceName: String, val spanStartEndEventsEnabled: Boolean, val copyBaggageToTags: Boolean) : TelemetryComponent {
val tracerSetup = TracerSetup(serviceName)
val tracer: Tracer = tracerSetup.getTracer()
companion object {
private val log: Logger = LoggerFactory.getLogger(OpenTelemetryComponent::class.java)
const val OPENTELEMETRY_COMPONENT_NAME = "OpenTelemetry"
}
val rootSpans = ConcurrentHashMap<UUID, SpanInfo>()
val spans = ConcurrentHashMap<UUID, SpanInfo>()
val baggages = ConcurrentHashMap<UUID, Scope>()
override fun isEnabled(): Boolean {
// DefaultTracer is the NoOp tracer in the OT API
return tracerSetup.getTracer().javaClass.name != "io.opentelemetry.api.trace.DefaultTracer"
}
override fun name(): String = OPENTELEMETRY_COMPONENT_NAME
override fun onTelemetryEvent(event: TelemetryEvent) {
when (event) {
is StartSpanForFlowEvent -> startSpanForFlow(event.name, event.attributes, event.telemetryId, event.flowLogic, event.telemetryDataItem)
is EndSpanForFlowEvent -> endSpanForFlow(event.telemetryId)
is StartSpanEvent -> startSpan(event.name, event.attributes, event.telemetryId, event.flowLogic)
is EndSpanEvent -> endSpan(event.telemetryId)
is SetStatusEvent -> setStatus(event.telemetryId, event.telemetryStatusCode, event.message)
is RecordExceptionEvent -> recordException(event.telemetryId, event.throwable)
is ShutdownTelemetryEvent -> shutdownTelemetry()
}
}
private fun shutdownTelemetry() {
tracerSetup.shutdown()
}
private fun extractContext(carrier: ContextCarrier): Context {
val getter = object : TextMapGetter<ContextCarrier?> {
override fun get(carrier: ContextCarrier?, key: String): String? {
return if (carrier?.context?.containsKey(key) == true) {
val value = carrier.context[key]
value
} else null
}
override fun keys(carrier: ContextCarrier?): MutableIterable<String> {
return carrier?.context?.keys ?: mutableListOf()
}
}
return carrier.let {
tracerSetup.openTelemetry.propagators.textMapPropagator.extract(Context.current(), it, getter)
}
}
@Suppress("LongParameterList")
private fun startSpanForFlow(name: String, attributes: Map<String, String>, telemetryId: UUID, flowLogic: FlowLogic<*>?,
telemetryDataItem: TelemetryDataItem?) {
val openTelemetryContext = telemetryDataItem as? OpenTelemetryContext
val extractedContext = openTelemetryContext?.let { extractContext(it.context) }
val spanEventContexts = openTelemetryContext?.let { SpanEventContexts(extractContext(it.spanEventChildContext), extractContext(it.spanEventParentContext)) }
val baggageAttributes = openTelemetryContext?.baggage?.let {
val baggageBuilder = it.toList().fold(Baggage.current().toBuilder()) {builder, attribute -> builder.put(attribute.first, attribute.second)}
baggages[telemetryId] = baggageBuilder.build().makeCurrent()
it
} ?: emptyMap()
val allAttributes = if (copyBaggageToTags) {
attributes + baggageAttributes
}
else {
attributes
}
val attributesMap = allAttributes.toList()
.fold(Attributes.builder()) { builder, attribute -> builder.put(attribute.first, attribute.second) }.also {
populateWithFlowAttributes(it, flowLogic)
}.build()
if (extractedContext != null && spanEventContexts != null) {
startSpanForFlowWithRemoteParent(name, attributesMap, telemetryId, extractedContext, spanEventContexts)
}
else {
startSpanForFlowWithNoParent(name, attributesMap, telemetryId)
}
}
private fun startSpanForFlowWithRemoteParent(name: String, attributesMap: Attributes, telemetryId: UUID, parentContext: Context, spanEventContexts: SpanEventContexts) {
val span = tracer.spanBuilder(name).setParent(parentContext).setAllAttributes(attributesMap).startSpan()
val spanScope = span.makeCurrent()
val contextAndQueue = startEndEventForFlowWithRemoteParent(name, attributesMap, spanEventContexts)
spans[telemetryId] = SpanInfo(name, span, spanScope, contextAndQueue?.first, contextAndQueue?.second)
}
private fun startEndEventForFlowWithRemoteParent(name: String, attributesMap: Attributes, spanEventContexts: SpanEventContexts): Pair<SpanEventContexts, ConcurrentLinkedDeque<SpanEventContexts>>? {
if (spanStartEndEventsEnabled) {
val contexts = createSpanToCaptureStartedSpanEventWithRemoteParent(name, spanEventContexts, attributesMap)
return Pair( contexts, ConcurrentLinkedDeque<SpanEventContexts>().also { it.add(contexts) })
}
return null
}
private fun startSpanForFlowWithNoParent(name: String, attributesMap: Attributes, telemetryId: UUID) {
val rootSpan = tracer.spanBuilder(name).setAllAttributes(attributesMap).setAllAttributes(Attributes.of(AttributeKey.stringKey("root.flow"), "true")).startSpan()
val rootSpanScope = rootSpan.makeCurrent()
if (spanStartEndEventsEnabled) {
val startedSpanContexts = createSpanToCaptureStartedSpanEvent(name, rootSpan, attributesMap)
val span = tracer.spanBuilder("Child Spans").setParent(Context.current().with(rootSpan)).startSpan()
val spanScope = span.makeCurrent()
rootSpans[telemetryId] = SpanInfo(name, rootSpan, rootSpanScope)
val spanEventContextStack = ConcurrentLinkedDeque<SpanEventContexts>().also { it.add(startedSpanContexts) }
spans[telemetryId] = SpanInfo(name, span, spanScope, startedSpanContexts, spanEventContextStack)
}
else {
spans[telemetryId] = SpanInfo(name, rootSpan, rootSpanScope)
}
}
private fun createSpanToCaptureStartedSpanEvent(name: String, rootSpan: Span, attributesMap: Attributes): SpanEventContexts {
val startedSpan = tracer.spanBuilder("Started Events").setAllAttributes(attributesMap).setAllAttributes(Attributes.of(AttributeKey.stringKey("root.startend.events"), "true")).setParent(Context.current().with(rootSpan)).startSpan()
val parentContext = Context.current().with(startedSpan)
startedSpan.end()
val startedSpanChild = tracer.spanBuilder("${name}-start").setAllAttributes(attributesMap)
.setParent(parentContext).startSpan()
val childContext = Context.current().with(startedSpanChild)
startedSpanChild.end()
return SpanEventContexts(childContext, parentContext)
}
private fun createSpanToCaptureStartedSpanEventWithRemoteParent(name: String, spanEventContexts: SpanEventContexts, attributesMap: Attributes ): SpanEventContexts {
val startedSpanChild = tracer.spanBuilder("${name}-start").setAllAttributes(attributesMap)
.setParent(spanEventContexts.child).startSpan()
val grandChildContext = Context.current().with(startedSpanChild)
startedSpanChild.end()
return SpanEventContexts(grandChildContext, spanEventContexts.child)
}
private fun endSpanForFlow(telemetryId: UUID){
val spanInfo = spans[telemetryId]
val rootSpanInfo = rootSpans[telemetryId]
if (spanStartEndEventsEnabled) {
createSpanToCaptureEndSpanEvent(spanInfo)
}
spanInfo?.spanScope?.close()
spanInfo?.span?.end()
rootSpanInfo?.spanScope?.close()
rootSpanInfo?.span?.end()
spans.remove(telemetryId)
rootSpans.remove(telemetryId)
val baggageScope = baggages[telemetryId]
baggageScope?.close()
baggages.remove(telemetryId)
}
private fun createSpanToCaptureEndSpanEvent(spanInfo: SpanInfo?) {
spanInfo?.spanEventContext?.parent?.let {
val startedSpanChild = tracer.spanBuilder("${spanInfo.name}-end").setParent(it).startSpan()
startedSpanChild.end()
}
val spanEventContextStack = spanInfo?.spanEventContextQueue
val filteredSpanEventContexts = spanEventContextStack?.filter { it == spanInfo.spanEventContext }
filteredSpanEventContexts?.forEach { spanEventContextStack.remove(it) }
}
private fun startSpan(name: String, attributes: Map<String, String>, telemetryId: UUID, flowLogic: FlowLogic<*>?) {
val currentBaggage = Baggage.current()
val baggageAttributes = mutableMapOf<String,String>()
if (copyBaggageToTags) {
currentBaggage.forEach { t, u -> baggageAttributes[t] = u.value }
}
val parentSpan = Span.current()
val attributesMap = (attributes+baggageAttributes).toList().fold(Attributes.builder()) { builder, attribute -> builder.put(attribute.first, attribute.second) }.also {
populateWithFlowAttributes(it, flowLogic)
}.build()
val span = tracer.spanBuilder(name).setAllAttributes(attributesMap).startSpan()
val spanScope = span.makeCurrent()
val spanEventContexts = createStartedEventSpan(name, attributesMap, parentSpan)
spans[telemetryId] = SpanInfo(name, span, spanScope, spanEventContexts?.peekLast(), spanEventContexts)
}
private fun populateWithFlowAttributes(attributesBuilder: AttributesBuilder, flowLogic: FlowLogic<*>?) {
flowLogic?.let {
attributesBuilder.put("flow.id", flowLogic.runId.uuid.toString())
attributesBuilder.put("creation.time", flowLogic.stateMachine.creationTime)
attributesBuilder.put("class.name", flowLogic.javaClass.name)
}
}
private fun createStartedEventSpan(name: String, attributesMap: Attributes, parentSpan: Span): ConcurrentLinkedDeque<SpanEventContexts>? {
return if (spanStartEndEventsEnabled) {
val filteredSpans = spans.filter { it.value.span == parentSpan }.toList()
val (startEventParentContext, spanEventContextQueue) = getStartEventParentContext(filteredSpans, parentSpan)
val startedSpanChild = tracer.spanBuilder("${name}-start").setAllAttributes(attributesMap)
.setParent(startEventParentContext).startSpan()
val childContext = Context.current().with(startedSpanChild)
startedSpanChild.end()
spanEventContextQueue?.offer(SpanEventContexts(childContext, startEventParentContext))
spanEventContextQueue
}
else {
null
}
}
private fun getStartEventParentContext(filteredSpans: List<Pair<UUID, SpanInfo>>, parentSpan: Span): Pair<Context, ConcurrentLinkedDeque<SpanEventContexts>?> {
return if (filteredSpans.isNotEmpty()) {
Pair(filteredSpans[0].second.spanEventContext?.child ?: Context.current(), filteredSpans[0].second.spanEventContextQueue)
}
else {
// Copes with case where user has created their own span. So we just use the most
// recent span we know about on the stack.
val altFilteredSpans = spans.filter { it.value.span.spanContext.traceId == parentSpan.spanContext.traceId }.toList()
val spanEventContexts = altFilteredSpans[0].second.spanEventContextQueue
Pair(spanEventContexts?.peekLast()?.child ?: Context.current(), spanEventContexts)
}
}
private fun endSpan(telemetryId: UUID){
val spanInfo = spans[telemetryId]
createSpanToCaptureEndSpanEvent(spanInfo)
spanInfo?.spanScope?.close()
spanInfo?.span?.end()
spans.remove(telemetryId)
}
override fun getCurrentTelemetryData(): TelemetryDataItem {
val currentSpan = Span.current()
val currentContextCarrier = inject(tracerSetup, Context.current())
val filteredSpans = spans.filter { it.value.span == currentSpan }.toList()
val childContext = if (filteredSpans.isNotEmpty()) {
filteredSpans.getOrNull(0)?.second?.spanEventContext?.child
}
else {
val altFilteredSpans = spans.filter { it.value.span.spanContext.traceId == currentSpan.spanContext.traceId }.toList()
if (altFilteredSpans.isNotEmpty()) {
altFilteredSpans[0].second.spanEventContextQueue?.peekLast()?.child
}
else {
null
}
}
val childContextCarrier = inject(tracerSetup, childContext)
val parentContext = if (filteredSpans.isNotEmpty()) {
filteredSpans.getOrNull(0)?.second?.spanEventContext?.parent
}
else {
val altFilteredSpans = spans.filter { it.value.span.spanContext.traceId == currentSpan.spanContext.traceId }.toList()
if (altFilteredSpans.isNotEmpty()) {
altFilteredSpans[0].second.spanEventContextQueue?.peekLast()?.parent
}
else {
null
}
}
val parentContextCarrier = inject(tracerSetup, parentContext)
return OpenTelemetryContext(currentContextCarrier, childContextCarrier, parentContextCarrier, Baggage.current().asMap().mapValues { it.value.value })
}
private fun inject(tracerSetup: TracerSetup, context: Context?) : ContextCarrier {
val contextCarrier = ContextCarrier(mutableMapOf())
context?.let { tracerSetup.openTelemetry.propagators.textMapPropagator.inject(it, contextCarrier) { carrier, key, value -> carrier?.context?.put(key, value) }}
return contextCarrier
}
override fun getCurrentTelemetryId(): UUID {
val currentSpan = Span.current()
val filteredSpans = spans.filter { it.value.span == currentSpan }.toList()
if (filteredSpans.isEmpty()) {
return UUID(0, 0)
}
return filteredSpans[0].first // return UUID associated with current span
}
override fun setCurrentTelemetryId(id: UUID) {
val spanInfo = spans[id]
spanInfo?.let {
it.spanScope.close() // close the old scope
val childSpanScope = it.span.makeCurrent()
val newSpanInfo = spanInfo.copy(spanScope = childSpanScope)
spans[id] = newSpanInfo
}
}
override fun getCurrentSpanId(): String {
return Span.current().spanContext.spanId
}
override fun getCurrentTraceId(): String {
return Span.current().spanContext.traceId
}
override fun getCurrentBaggage(): Map<String, String> {
return Baggage.current().asMap().mapValues { it.value.value }
}
override fun getTelemetryHandles(): List<Any> {
return listOf(tracerSetup.openTelemetry)
}
private fun setStatus(telemetryId: UUID, telemetryStatusCode: TelemetryStatusCode, message: String) {
val spanInfo = spans[telemetryId]
spanInfo?.span?.setStatus(toOpenTelemetryStatus(telemetryStatusCode), message)
}
private fun toOpenTelemetryStatus(telemetryStatusCode: TelemetryStatusCode): StatusCode {
return when(telemetryStatusCode) {
TelemetryStatusCode.ERROR -> StatusCode.ERROR
TelemetryStatusCode.OK -> StatusCode.OK
TelemetryStatusCode.UNSET -> StatusCode.UNSET
}
}
private fun recordException(telemetryId: UUID, throwable: Throwable) {
val spanInfo = spans[telemetryId]
spanInfo?.span?.recordException(throwable)
}
}

View File

@ -0,0 +1,129 @@
package net.corda.core.internal.telemetry
import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.slf4j.MDC
import java.lang.IllegalStateException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@CordaSerializable
data class SimpleLogContext(val traceId: UUID, val baggage: Map<String, String>): TelemetryDataItem
const val CLIENT_ID = "client.id"
const val TRACE_ID = "trace.id"
// Simple telemetry class that creates a single UUID and uses this for the trace id. When the flow starts we use the trace is passed in. After this
// though we must use the trace id propagated to us (if remote), or the trace id associated with thread local.
@Suppress("TooManyFunctions")
class SimpleLogTelemetryComponent : TelemetryComponent {
companion object {
private val log: Logger = LoggerFactory.getLogger(SimpleLogTelemetryComponent::class.java)
}
private val traces: InheritableThreadLocal<UUID> = InheritableThreadLocal()
private val logContexts = ConcurrentHashMap<UUID, SimpleLogContext>()
override fun isEnabled(): Boolean {
return true
}
override fun name(): String = "SimpleLogTelemetry"
override fun onTelemetryEvent(event: TelemetryEvent) {
when (event) {
is StartSpanForFlowEvent -> startSpanForFlow(event.name, event.attributes, event.telemetryId, event.flowLogic, event.telemetryDataItem)
is EndSpanForFlowEvent -> endSpanForFlow(event.telemetryId)
is StartSpanEvent -> startSpan(event.name, event.attributes, event.telemetryId, event.flowLogic)
is EndSpanEvent -> endSpan(event.telemetryId)
is SetStatusEvent -> setStatus(event.telemetryId, event.telemetryStatusCode, event.message)
is RecordExceptionEvent -> recordException(event.telemetryId, event.throwable)
}
}
@Suppress("LongParameterList")
private fun startSpanForFlow(name: String, attributes: Map<String, String>, telemetryId: UUID, flowLogic: FlowLogic<*>?, telemetryDataItem: TelemetryDataItem?) {
val simpleLogTelemetryDataItem = telemetryDataItem?.let {(telemetryDataItem as? SimpleLogContext) ?:
throw IllegalStateException("Type of telemetryDataItem no a SimpleLogContext, actual class is ${telemetryDataItem::class.java.name}")}
val traceId = simpleLogTelemetryDataItem?.traceId ?: telemetryId
val flowId = flowLogic?.runId
val clientId = simpleLogTelemetryDataItem?.baggage?.get(CLIENT_ID) ?: flowLogic?.stateMachine?.clientId
traces.set(traceId)
val baggageAttributes = simpleLogTelemetryDataItem?.baggage ?: emptyMap()
logContexts[traceId] = SimpleLogContext(traceId, baggageAttributes)
clientId?.let { MDC.put(CLIENT_ID, it) }
MDC.put(TRACE_ID, traceId.toString())
log.info("startSpanForFlow: name: $name, traceId: $traceId, flowId: $flowId, clientId: $clientId, attributes: ${attributes+baggageAttributes}")
}
// Check when you start a top level flow the startSpanForFlow appears just once, and so the endSpanForFlow also appears just once
// So its valid to do the MDC clear here. For remotes nodes as well
private fun endSpanForFlow(telemetryId: UUID) {
log.info("endSpanForFlow: traceId: ${traces.get()}")
logContexts.remove(telemetryId)
MDC.clear()
}
@Suppress("UNUSED_PARAMETER")
private fun startSpan(name: String, attributes: Map<String, String>, telemetryId: UUID, flowLogic: FlowLogic<*>?) {
val flowId = flowLogic?.runId
val clientId = flowLogic?.stateMachine?.clientId
val traceId = traces.get()
log.info("startSpan: name: $name, traceId: $traceId, flowId: $flowId, clientId: $clientId, attributes: $attributes")
}
@Suppress("UNUSED_PARAMETER")
private fun endSpan(telemetryId: UUID) {
log.info("endSpan: traceId: ${traces.get()}")
}
override fun getCurrentTelemetryData(): SimpleLogContext {
traces.get()?.let {
logContexts[it]?.let { simpleLogContext ->
return simpleLogContext
}
}
return SimpleLogContext(UUID(0, 0), emptyMap())
}
override fun getCurrentTelemetryId(): UUID {
return traces.get() ?: UUID(0,0)
}
override fun setCurrentTelemetryId(id: UUID) {
traces.set(id)
}
override fun getCurrentSpanId(): String {
return traces.get()?.toString() ?: ""
}
override fun getCurrentTraceId(): String {
return traces.get()?.toString() ?: ""
}
override fun getCurrentBaggage(): Map<String, String> {
val uuid = traces.get()
return logContexts[uuid]?.baggage ?: emptyMap()
}
override fun getTelemetryHandles(): List<Any> {
return emptyList()
}
@Suppress("UNUSED_PARAMETER")
private fun setStatus(telemetryId: UUID, telemetryStatusCode: TelemetryStatusCode, message: String) {
when(telemetryStatusCode) {
TelemetryStatusCode.ERROR -> log.error("setStatus: traceId: ${traces.get()}, statusCode: ${telemetryStatusCode}, message: message")
TelemetryStatusCode.OK, TelemetryStatusCode.UNSET -> log.info("setStatus: traceId: ${traces.get()}, statusCode: ${telemetryStatusCode}, message: message")
}
}
@Suppress("UNUSED_PARAMETER")
private fun recordException(telemetryId: UUID, throwable: Throwable) {
log.error("recordException: traceId: ${traces.get()}, throwable: ${throwable}}")
}
}

View File

@ -0,0 +1,266 @@
package net.corda.core.internal.telemetry
import net.corda.core.CordaInternal
import net.corda.core.DeleteForDJVM
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.uncheckedCast
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.TelemetryService
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationFactory
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*
@CordaSerializable
interface TelemetryDataItem
@CordaSerializable
data class SerializedTelemetry(val serializedTelemetryData: Map<String, OpaqueBytes>)
enum class TelemetryStatusCode {
/** The default status. */
UNSET,
/**
* The operation has been validated by an Application developers or Operator to have completed
* successfully.
*/
OK,
/** The operation contains an error. */
ERROR
}
@DeleteForDJVM
@CordaSerializable
data class TelemetryId(private val telemetryService: TelemetryServiceImpl) {
val id: UUID = UUID.randomUUID()
fun setStatus(telemetryStatusCode: TelemetryStatusCode, message: String) {
telemetryService.setStatus(this, telemetryStatusCode, message)
}
fun recordException(throwable: Throwable) {
telemetryService.recordException( this, throwable)
}
fun close() {
telemetryService.endSpan(this)
}
}
@CordaSerializable
data class ComponentTelemetryIds(val componentTelemetryIds: Map<String, UUID>)
interface TelemetryEvent
@DeleteForDJVM
class StartSpanForFlowEvent(val name: String,
val attributes: Map<String, String>,
val telemetryId: UUID, val flowLogic: FlowLogic<*>?,
val telemetryDataItem: TelemetryDataItem?): TelemetryEvent
@DeleteForDJVM
class EndSpanForFlowEvent(val telemetryId: UUID): TelemetryEvent
@DeleteForDJVM
class StartSpanEvent(val name: String, val attributes: Map<String, String>, val telemetryId: UUID, val flowLogic: FlowLogic<*>?): TelemetryEvent
class EndSpanEvent(val telemetryId: UUID): TelemetryEvent
class SetStatusEvent(val telemetryId: UUID, val telemetryStatusCode: TelemetryStatusCode, val message: String): TelemetryEvent
class RecordExceptionEvent(val telemetryId: UUID, val throwable: Throwable): TelemetryEvent
class InitialiseTelemetryEvent: TelemetryEvent
class ShutdownTelemetryEvent: TelemetryEvent
interface TelemetryComponent {
fun name(): String
fun isEnabled(): Boolean
fun onTelemetryEvent(event: TelemetryEvent)
fun getCurrentTelemetryData(): TelemetryDataItem
fun getCurrentTelemetryId(): UUID
fun setCurrentTelemetryId(id: UUID)
fun getCurrentSpanId(): String
fun getCurrentTraceId(): String
fun getCurrentBaggage(): Map<String, String>
fun getTelemetryHandles(): List<Any>
}
interface TelemetryComponentId {
fun name(): String
}
@Suppress("TooManyFunctions")
@DeleteForDJVM
class TelemetryServiceImpl : SingletonSerializeAsToken(), TelemetryService {
companion object {
private val log: Logger = LoggerFactory.getLogger(TelemetryServiceImpl::class.java)
}
fun getCurrentSpanId(telemetryComponentName: String): String? {
return telemetryComponents[telemetryComponentName]?.getCurrentSpanId()
}
fun getCurrentTraceId(telemetryComponentName: String): String? {
return telemetryComponents[telemetryComponentName]?.getCurrentTraceId()
}
fun getCurrentBaggage(telemetryComponentName: String): Map<String, String>? {
return telemetryComponents[telemetryComponentName]?.getCurrentBaggage()
}
fun setStatus(telemetryId: TelemetryId, telemetryStatusCode: TelemetryStatusCode, message: String) {
telemetryComponents.values.forEach {
it.onTelemetryEvent(SetStatusEvent(telemetryId.id, telemetryStatusCode, message))
}
}
fun recordException(telemetryId: TelemetryId, throwable: Throwable) {
telemetryComponents.values.forEach {
it.onTelemetryEvent(RecordExceptionEvent(telemetryId.id, throwable))
}
}
@CordaInternal
fun deserialize(data: OpaqueBytes): TelemetryDataItem {
return SerializationFactory.defaultFactory.deserialize(data, TelemetryDataItem::class.java, SerializationFactory.defaultFactory.defaultContext)
}
private val telemetryComponents: MutableMap<String, TelemetryComponent> = mutableMapOf()
@CordaInternal
fun initialiseTelemetry() {
telemetryComponents.values.forEach {
it.onTelemetryEvent(InitialiseTelemetryEvent())
}
}
@CordaInternal
fun shutdownTelemetry() {
telemetryComponents.values.forEach {
it.onTelemetryEvent(ShutdownTelemetryEvent())
}
telemetryComponents.clear()
}
@CordaInternal
fun addTelemetryComponent(telemetryComponent: TelemetryComponent) {
telemetryComponents[telemetryComponent.name()] = telemetryComponent
}
@CordaInternal
fun startSpanForFlow(name: String, attributes: Map<String, String>, flowLogic: FlowLogic<*>? = null, remoteSerializedTelemetry: SerializedTelemetry? = null): TelemetryId {
val telemetryId = TelemetryId(this)
telemetryComponents.values.forEach {
val bytes = remoteSerializedTelemetry?.serializedTelemetryData?.get(it.name())
val telemetryDataItem = bytes?.let { deserialize(bytes) }
it.onTelemetryEvent(StartSpanForFlowEvent(name, attributes, telemetryId.id, flowLogic, telemetryDataItem))
}
return telemetryId
}
@CordaInternal
fun endSpanForFlow(telemetryId: TelemetryId) {
telemetryComponents.values.forEach {
it.onTelemetryEvent(EndSpanForFlowEvent(telemetryId.id))
}
}
fun startSpan(name: String, attributes: Map<String, String> = emptyMap(), flowLogic: FlowLogic<*>? = null): TelemetryId {
val telemetryId = TelemetryId(this)
telemetryComponents.values.forEach {
it.onTelemetryEvent(StartSpanEvent(name, attributes, telemetryId.id, flowLogic))
}
return telemetryId
}
fun endSpan(telemetryId: TelemetryId) {
telemetryComponents.values.forEach {
it.onTelemetryEvent(EndSpanEvent(telemetryId.id))
}
}
@Suppress("TooGenericExceptionCaught")
inline fun <R> span(name: String, attributes: Map<String, String> = emptyMap(), flowLogic: FlowLogic<*>? = null, block: () -> R): R {
val telemetryId = startSpan(name, attributes, flowLogic)
try {
return block()
}
catch(ex: Throwable) {
recordException(telemetryId, ex)
setStatus(telemetryId, TelemetryStatusCode.ERROR, "Exception raised: ${ex.message}")
throw ex
}
finally {
endSpan(telemetryId)
}
}
@CordaInternal
@Suppress("LongParameterList", "TooGenericExceptionCaught")
inline fun <R> spanForFlow(name: String, attributes: Map<String, String>, flowLogic: FlowLogic<*>? = null, remoteSerializedTelemetry: SerializedTelemetry? = null, block: () -> R): R {
val telemetryId = startSpanForFlow(name, attributes, flowLogic, remoteSerializedTelemetry)
try {
return block()
}
catch(ex: Throwable) {
recordException(telemetryId, ex)
setStatus(telemetryId, TelemetryStatusCode.ERROR, "Exception raised: ${ex.message}")
throw ex
}
finally {
endSpanForFlow(telemetryId)
}
}
@CordaInternal
fun getCurrentTelemetryData(): SerializedTelemetry? {
if (telemetryComponents.isEmpty()) {
return null
}
val serializedTelemetryData = mutableMapOf<String, OpaqueBytes>()
telemetryComponents.values.forEach {
val currentTelemetryData = it.getCurrentTelemetryData()
serializedTelemetryData[it.name()] = currentTelemetryData.serialize()
}
return SerializedTelemetry(serializedTelemetryData)
}
@CordaInternal
fun getCurrentTelemetryIds(): ComponentTelemetryIds? {
if (telemetryComponents.isEmpty()) {
return null
}
val telemetryIds = mutableMapOf<String, UUID>()
telemetryComponents.values.forEach {
telemetryIds[it.name()] = it.getCurrentTelemetryId()
}
return ComponentTelemetryIds(telemetryIds)
}
@CordaInternal
fun setCurrentTelemetryId(telemetryIds: ComponentTelemetryIds) {
telemetryComponents.values.forEach {
it.setCurrentTelemetryId(telemetryIds.componentTelemetryIds[it.name()]!!)
}
}
private fun getTelemetryHandles(): List<Any> {
return telemetryComponents.values.map { it.getTelemetryHandles() }.flatten()
}
override fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
getTelemetryHandles().forEach {
if (telemetryClass.isInstance(it))
@Suppress("UNCHECKED_CAST")
return uncheckedCast(it as T)
}
return null
}
}
val ServiceHub.telemetryServiceInternal
get() = this.telemetryService as TelemetryServiceImpl

View File

@ -0,0 +1,24 @@
package net.corda.core.internal.utilities
import net.corda.core.CordaInternal
import net.corda.core.KeepForDJVM
@KeepForDJVM
interface Internable<T> {
@CordaInternal
val interner: PrivateInterner<T>
}
@KeepForDJVM
@CordaInternal
interface IternabilityVerifier<T> {
// If a type being interned has a slightly dodgy equality check, the more strict rules you probably
// want to apply to interning can be enforced here.
fun choose(original: T, interned: T): T
}
@KeepForDJVM
@CordaInternal
class AlwaysInternableVerifier<T> : IternabilityVerifier<T> {
override fun choose(original: T, interned: T): T = interned
}

View File

@ -0,0 +1,69 @@
package net.corda.core.internal.utilities
import com.google.common.collect.Interners
import net.corda.core.CordaInternal
import net.corda.core.internal.packageNameOrNull
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.CordaSerializable
import kotlin.reflect.full.companionObjectInstance
/**
* This class converts instances supplied to [intern] to a common instance within the JVM, amongst all those
* instances that have been submitted. It uses weak references to avoid memory leaks.
*
* NOTE: the Guava interners are Beta, so upgrading Guava may result in us having to adapt this code.
*
* System properties allow disabling, in the event an issue is uncovered in a live environment. The
* correct default for the concurrency setting is the result of performance evaluation.
*/
@CordaInternal
class PrivateInterner<T>(val verifier: IternabilityVerifier<T> = AlwaysInternableVerifier()) {
companion object {
// This value is the default in Guava, and performance testing didn't reveal a need to change
private const val DEFAULT_CONCURRENCY_LEVEL = 4
private val CONCURRENCY_LEVEL = Integer.getInteger("net.corda.core.intern.concurrency", DEFAULT_CONCURRENCY_LEVEL).toInt()
private val DISABLE = java.lang.Boolean.getBoolean("net.corda.core.intern.disable")
/**
* This will look at the companion object of a class, and on the super class companion object,
* to see if they implement [Internable], in which case there is a [PrivateInterner] instance
* available to do interning. Tolerant of null class references and a lack of companion objects.
*/
@Suppress("ComplexMethod")
fun findFor(clazz: Class<*>?): PrivateInterner<Any>? {
fun hasCordaSerializable(type: Class<*>): Boolean {
return type.isAnnotationPresent(CordaSerializable::class.java)
|| type.interfaces.any(::hasCordaSerializable)
|| (type.superclass != null && hasCordaSerializable(type.superclass))
}
fun isSerializableCore(clazz: Class<*>): Boolean {
if (!(clazz.packageNameOrNull?.startsWith("net.corda.core") ?: false)) return false
return hasCordaSerializable(clazz)
}
fun findInterner(clazz: Class<*>?): PrivateInterner<Any>? {
// Kotlin reflection has a habit of throwing exceptions, so protect just in case.
try {
return clazz?.kotlin?.companionObjectInstance?.let {
(it as? Internable<*>)?.let {
uncheckedCast(it.interner)
}
}
} catch (_: Throwable) {
return null
}
}
return if (clazz != null) {
// We try not to ruffle the feathers of kotlin reflection by avoiding throwing all types at it.
if (!isSerializableCore(clazz)) return null
findInterner(clazz) ?: findInterner(clazz.superclass)
} else null
}
}
private val interner = Interners.newBuilder().weak().concurrencyLevel(CONCURRENCY_LEVEL).build<T>()
fun <S : T> intern(sample: S): S = if (DISABLE) sample else uncheckedCast(verifier.choose(sample, interner.intern(sample)))
}

View File

@ -392,7 +392,11 @@ interface CordaRPCOps : RPCOps {
/** Queries attachments metadata */
fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId>
/** Returns the node's current time. */
/** Returns the node's current time.
*
* Is a quick RPC, meaning that it is handled outside the node's standard thread pool in order to provide a
* quick response even when the node is dealing with a high volume of RPC calls.
*/
fun currentNodeTime(): Instant
/**

View File

@ -8,6 +8,10 @@ import net.corda.core.DoNotImplement
*/
@DoNotImplement
interface RPCOps {
/** Returns the RPC protocol version. Exists since version 0 so guaranteed to be present. */
/** Returns the RPC protocol version. Exists since version 0 so guaranteed to be present.
*
* Getting this property is handled as a quick RPC, meaning that it is handled outside the node's standard
* thread pool in order to provide a quick response even when the node is dealing with a high volume of RPC calls.
*/
val protocolVersion: Int
}

View File

@ -12,6 +12,7 @@ import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.node.services.*
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.LedgerTransaction
@ -165,6 +166,11 @@ interface ServiceHub : ServicesForResolution {
*/
val diagnosticsService: DiagnosticsService
/**
* Provides operations to support telemetry and telemetry data between nodes.
*/
val telemetryService: TelemetryService
/**
* INTERNAL. DO NOT USE.
* @suppress
@ -187,6 +193,13 @@ interface ServiceHub : ServicesForResolution {
*/
fun <T : SerializeAsToken> cordaService(type: Class<T>): T
/**
* Return the singleton instance of the given Corda telemetry component type. This is a class that implements TelemetryComponent
* and will have automatically been registered by the node.
* @throws IllegalArgumentException If the instance is not found.
*/
fun <T : TelemetryComponent> cordaTelemetryComponent(type: Class<T>): T
/**
* Stores the given [SignedTransaction]s in the local transaction storage and then sends them to the vault for
* further processing if [notifyVault] is true. This is expected to be run within a database transaction.

View File

@ -17,6 +17,18 @@ interface ServiceLifecycleObserver {
}
enum class ServiceLifecycleEvent {
/**
* This event is dispatched when the node is about to start the State Machine. The State Machine will not be started until
* all handlers return from processing this event.
* Handlers can delay the node start-up, and the processing of flows, by not returning from handling this event until their
* CorDapp is ready for flows to be started/resumed.
*
* If a handler for this event throws [CordaServiceCriticalFailureException] - this is the way to flag that it will not make
* sense for Corda node to continue its operation. The lifecycle events dispatcher will endeavor to terminate node's JVM as soon
* as practically possible.
*/
BEFORE_STATE_MACHINE_START,
/**
* This event is dispatched when State Machine is fully started such that [net.corda.core.node.AppServiceHub] available
* for [CordaService] to be use.

View File

@ -0,0 +1,9 @@
package net.corda.core.node.services
import net.corda.core.DoNotImplement
@DoNotImplement
interface TelemetryService {
fun <T> getTelemetryHandle(telemetryClass: Class<T>): T?
}

View File

@ -178,7 +178,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
Type.ALWAYS_ACCEPT -> ConstraintInfo(AlwaysAcceptAttachmentConstraint)
Type.HASH -> ConstraintInfo(HashAttachmentConstraint(SecureHash.create(data!!.toHexString())))
Type.CZ_WHITELISTED -> ConstraintInfo(WhitelistedByZoneAttachmentConstraint)
Type.SIGNATURE -> ConstraintInfo(SignatureAttachmentConstraint(Crypto.decodePublicKey(data!!)))
Type.SIGNATURE -> ConstraintInfo(SignatureAttachmentConstraint.create(Crypto.decodePublicKey(data!!)))
}
}
}

View File

@ -51,6 +51,9 @@ import java.util.WeakHashMap
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
import java.util.function.Function
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
/**
* A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only
@ -168,7 +171,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
if(read <= 0) break
md.update(ctx.buffer, 0, read)
}
return SecureHash.SHA256(md.digest())
return SecureHash.createSHA256(md.digest())
}
private fun isZipOrJar(attachment: Attachment) = attachment.openAsJAR().use { jar ->

View File

@ -686,7 +686,8 @@ open class TransactionBuilder(
}
private fun makeSignatureAttachmentConstraint(attachmentSigners: List<PublicKey>) =
SignatureAttachmentConstraint(CompositeKey.Builder().addKeys(attachmentSigners).build())
SignatureAttachmentConstraint.create(CompositeKey.Builder().addKeys(attachmentSigners)
.build())
private fun getInstalledContractAttachmentId(
contractClassName: String,

View File

@ -26,7 +26,7 @@ results in
If no image variant is specified, all available image variants will be built.
The default repository for all images is `corda/corda` and you will need official R3 credentials
for Artifactory to push there. [Ross Nicoll](ross.nicoll@r3.com) (or other Artifactory administrators) can assist with this if needed,
for Artifactory to push there. R3's Artifactory administrators can assist with this if needed,
otherwise you can override the repository name using the `docker.image.repository` Gradle property.
e.g.

View File

@ -1,8 +1,8 @@
#!/usr/bin/env bash
NODE_LIST=("dockerNode1" "dockerNode2" "dockerNode3")
NETWORK_NAME=mininet
CORDAPP_VERSION="4.9-SNAPSHOT"
DOCKER_IMAGE_VERSION="corda-zulu-4.9-snapshot"
CORDAPP_VERSION="4.10-SNAPSHOT"
DOCKER_IMAGE_VERSION="corda-zulu-4.10-snapshot"
mkdir cordapps
rm -f cordapps/*

View File

@ -6,7 +6,9 @@ import net.corda.core.context.Trace
import net.corda.core.context.Trace.InvocationId
import net.corda.core.context.Trace.SessionId
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.utilities.Id
@ -116,7 +118,8 @@ object RPCApi {
val replyId: InvocationId,
val sessionId: SessionId,
val externalTrace: Trace? = null,
val impersonatedActor: Actor? = null
val impersonatedActor: Actor? = null,
val serializedTelemetry: SerializedTelemetry? = null
) : ClientToServer() {
override fun writeToClientMessage(message: ClientMessage) {
MessageUtil.setJMSReplyTo(message, clientAddress)
@ -130,6 +133,8 @@ object RPCApi {
message.putStringProperty(METHOD_NAME_FIELD_NAME, methodName)
message.bodyBuffer.writeBytes(serialisedArguments.bytes)
val telemetryBytes: SerializedBytes<SerializedTelemetry>? = serializedTelemetry?.serialize()
telemetryBytes?.let { message.putBytesProperty(TELEMETRY_PROPERTY, it.bytes) }
}
}
@ -148,15 +153,20 @@ object RPCApi {
fun fromClientMessage(message: ClientMessage): ClientToServer {
val tag = Tag.values()[message.getIntProperty(TAG_FIELD_NAME)]
return when (tag) {
RPCApi.ClientToServer.Tag.RPC_REQUEST -> RpcRequest(
RPCApi.ClientToServer.Tag.RPC_REQUEST -> {
val telemetryBytes = message.getBytesProperty(TELEMETRY_PROPERTY)
val serializedTelemetry: SerializedTelemetry? = telemetryBytes?.let {OpaqueBytes(it).deserialize()}
RpcRequest(
clientAddress = MessageUtil.getJMSReplyTo(message),
methodName = message.getStringProperty(METHOD_NAME_FIELD_NAME),
serialisedArguments = OpaqueBytes(message.getBodyAsByteArray()),
replyId = message.replyId(),
sessionId = message.sessionId(),
externalTrace = message.externalTrace(),
impersonatedActor = message.impersonatedActor()
impersonatedActor = message.impersonatedActor(),
serializedTelemetry = serializedTelemetry
)
}
RPCApi.ClientToServer.Tag.OBSERVABLES_CLOSED -> {
val ids = ArrayList<InvocationId>()
val buffer = message.bodyBuffer
@ -279,6 +289,7 @@ private const val DEDUPLICATION_IDENTITY_FIELD_NAME = "deduplication-identity"
private const val OBSERVABLE_ID_FIELD_NAME = "observable-id"
private const val OBSERVABLE_ID_TIMESTAMP_FIELD_NAME = "observable-id-timestamp"
private const val METHOD_NAME_FIELD_NAME = "method-name"
private const val TELEMETRY_PROPERTY = "telemetry-data"
fun ClientMessage.replyId(): InvocationId {
@ -301,6 +312,11 @@ fun ClientMessage.externalTrace(): Trace? {
}
}
fun ClientMessage.serializedTelemetry(): SerializedTelemetry? {
val telemetryBytes = this.getBytesProperty(TELEMETRY_PROPERTY)
return telemetryBytes?.let { OpaqueBytes(it).deserialize() }
}
fun ClientMessage.impersonatedActor(): Actor? {
return getStringProperty(RPC_IMPERSONATED_ACTOR_ID)?.let {

View File

@ -0,0 +1,8 @@
package net.corda.nodeapi.internal
enum class NodeStatus {
WAITING_TO_START,
STARTING,
STARTED,
STOPPING
}

View File

@ -49,6 +49,7 @@ interface NodeLifecycleObserver {
sealed class NodeLifecycleEvent(val reversedPriority: Boolean = false) {
class BeforeNodeStart(val nodeInitialContext: NodeInitialContext) : NodeLifecycleEvent()
class AfterNodeStart<out T : NodeServicesContext>(val nodeServicesContext: T) : NodeLifecycleEvent()
class BeforeStateMachineStart<out T : NodeServicesContext>(val nodeServicesContext: T) : NodeLifecycleEvent()
class StateMachineStarted<out T : NodeServicesContext>(val nodeServicesContext: T) : NodeLifecycleEvent()
class StateMachineStopped<out T : NodeServicesContext>(val nodeServicesContext: T) : NodeLifecycleEvent(reversedPriority = true)
class BeforeNodeStop<out T : NodeServicesContext>(val nodeServicesContext: T) : NodeLifecycleEvent(reversedPriority = true)

View File

@ -58,7 +58,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
private var remoteCert: X509Certificate? = null
private var eventProcessor: EventProcessor? = null
private var suppressClose: Boolean = false
private var badCert: Boolean = false
private var connectionResult: ConnectionResult = ConnectionResult.NO_ERROR
private var localCert: X509Certificate? = null
private var requestedServerName: String? = null
@ -130,7 +130,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
val ch = ctx.channel()
logInfoWithMDC { "Closed client connection ${ch.id()} from $remoteAddress to ${ch.localAddress()}" }
if (!suppressClose) {
onClose(ch as SocketChannel, ConnectionChange(remoteAddress, remoteCert, false, badCert))
onClose(ch as SocketChannel, ConnectionChange(remoteAddress, remoteCert, false, connectionResult))
}
eventProcessor?.close()
ctx.fireChannelInactive()
@ -273,13 +273,13 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
val remoteX500Name = try {
CordaX500Name.build(remoteCert!!.subjectX500Principal)
} catch (ex: IllegalArgumentException) {
badCert = true
connectionResult = ConnectionResult.HANDSHAKE_FAILURE
logErrorWithMDC("Certificate subject not a valid CordaX500Name", ex)
ctx.close()
return
}
if (allowedRemoteLegalNames != null && remoteX500Name !in allowedRemoteLegalNames) {
badCert = true
connectionResult = ConnectionResult.HANDSHAKE_FAILURE
logErrorWithMDC("Provided certificate subject $remoteX500Name not in expected set $allowedRemoteLegalNames")
ctx.close()
return
@ -287,7 +287,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
logInfoWithMDC { "Handshake completed with subject: $remoteX500Name, requested server name: ${sslHandler.getRequestedServerName()}." }
createAMQPEngine(ctx)
onOpen(ctx.channel() as SocketChannel, ConnectionChange(remoteAddress, remoteCert, connected = true, badCert = false))
onOpen(ctx.channel() as SocketChannel, ConnectionChange(remoteAddress, remoteCert, connected = true, connectionResult = ConnectionResult.NO_ERROR))
}
private fun handleFailedHandshake(ctx: ChannelHandlerContext, evt: SslHandshakeCompletionEvent) {
@ -301,7 +301,7 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
// io.netty.handler.ssl.SslHandler.setHandshakeFailureTransportFailure()
cause is SSLException && (cause.message?.contains("writing TLS control frames") == true) -> logWarnWithMDC(cause.message!!)
cause is SSLException && (cause.message?.contains("internal_error") == true) -> logWarnWithMDC("Received internal_error during handshake")
else -> badCert = true
else -> connectionResult = ConnectionResult.HANDSHAKE_FAILURE
}
if (log.isTraceEnabled) {
withMDC { log.trace("Handshake failure", cause) }

View File

@ -35,6 +35,7 @@ import java.net.InetSocketAddress
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.ThreadPoolExecutor
import java.time.Duration
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
@ -78,6 +79,7 @@ class AMQPClient(private val targets: List<NetworkHostAndPort>,
private const val MAX_RETRY_INTERVAL = 60000L
private const val BACKOFF_MULTIPLIER = 2L
private val NUM_CLIENT_THREADS = Integer.getInteger(CORDA_AMQP_NUM_CLIENT_THREAD_PROP_NAME, 2)
private val handshakeRetryIntervals = List(5) { Duration.ofMinutes(5) }
}
private val lock = ReentrantLock()
@ -89,7 +91,9 @@ class AMQPClient(private val targets: List<NetworkHostAndPort>,
private var targetIndex = 0
private var currentTarget: NetworkHostAndPort = targets.first()
private var retryInterval = MIN_RETRY_INTERVAL
private val badCertTargets = mutableSetOf<NetworkHostAndPort>()
private val handshakeFailureRetryTargets = mutableSetOf<NetworkHostAndPort>()
private var retryingHandshakeFailures = false
private var retryOffset = 0
@Volatile
private var amqpActive = false
@Volatile
@ -98,22 +102,67 @@ class AMQPClient(private val targets: List<NetworkHostAndPort>,
val localAddressString: String
get() = clientChannel?.localAddress()?.toString() ?: "<unknownLocalAddress>"
private fun nextTarget() {
/*
Figure out the index of the next address to try to connect to
*/
private fun setTargetIndex() {
val origIndex = targetIndex
targetIndex = -1
for (offset in 1..targets.size) {
val newTargetIndex = (origIndex + offset).rem(targets.size)
if (targets[newTargetIndex] !in badCertTargets) {
if (targets[newTargetIndex] !in handshakeFailureRetryTargets ) {
targetIndex = newTargetIndex
break
}
}
}
/*
Set how long to wait until trying to connect to the next address
*/
private fun setTargetRetryInterval() {
retryInterval = if (retryingHandshakeFailures) {
if (retryOffset < handshakeRetryIntervals.size) {
handshakeRetryIntervals[retryOffset++].toMillis()
} else {
Duration.ofDays(1).toMillis()
}
} else {
min(MAX_RETRY_INTERVAL, retryInterval * BACKOFF_MULTIPLIER)
}
}
/*
Once a connection is made, reset all the retry-connection info so if there is another connection failure
then this node tries to reconnect quickly.
*/
private fun successfullyConnected() {
log.info("Successfully connected to [${targets[targetIndex]}]; resetting the target connection-retry interval")
retryingHandshakeFailures = false
retryInterval = MIN_RETRY_INTERVAL
retryOffset = 0
}
/*
Set the next target to connect to
*/
private fun nextTarget() {
setTargetIndex()
if (targetIndex == -1) {
log.error("No targets have presented acceptable certificates for $allowedRemoteLegalNames. Halting retries")
if (handshakeFailureRetryTargets.isNotEmpty()) {
log.info("Failed to connect to any targets. Retrying targets that previously failed to handshake.")
handshakeFailureRetryTargets.clear()
retryingHandshakeFailures = true
setTargetIndex()
} else {
log.error("Attempted connection to targets: $targets, but none of them have presented acceptable certificates" +
" for $allowedRemoteLegalNames. Halting retries.")
return
}
log.info("Retry connect to ${targets[targetIndex]}")
retryInterval = min(MAX_RETRY_INTERVAL, retryInterval * BACKOFF_MULTIPLIER)
}
setTargetRetryInterval()
log.info("Retry connect to ${targets[targetIndex]} in [$retryInterval] ms")
}
private val connectListener = ChannelFutureListener { future ->
@ -238,7 +287,7 @@ class AMQPClient(private val targets: List<NetworkHostAndPort>,
private fun onChannelOpen(change: ConnectionChange) {
parent.run {
amqpActive = true
retryInterval = MIN_RETRY_INTERVAL // reset to fast reconnect if we connect properly
successfullyConnected()
_onConnection.onNext(change)
}
}
@ -247,9 +296,9 @@ class AMQPClient(private val targets: List<NetworkHostAndPort>,
if (parent.amqpChannelHandler != amqpChannelHandler) return
parent.run {
_onConnection.onNext(change)
if (change.badCert) {
log.error("Blocking future connection attempts to $target due to bad certificate on endpoint")
badCertTargets += target
if (change.connectionResult == ConnectionResult.HANDSHAKE_FAILURE) {
log.warn("Handshake failure with $target target; will retry later")
handshakeFailureRetryTargets += target
}
if (started && amqpActive) {

View File

@ -3,8 +3,8 @@ package net.corda.nodeapi.internal.protonwrapper.netty
import java.net.InetSocketAddress
import java.security.cert.X509Certificate
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val badCert: Boolean) {
data class ConnectionChange(val remoteAddress: InetSocketAddress, val remoteCert: X509Certificate?, val connected: Boolean, val connectionResult: ConnectionResult) {
override fun toString(): String {
return "ConnectionChange remoteAddress: $remoteAddress connected state: $connected cert subject: ${remoteCert?.subjectDN} cert ok: ${!badCert}"
return "ConnectionChange remoteAddress: $remoteAddress connected state: $connected cert subject: ${remoteCert?.subjectDN} result: ${connectionResult}"
}
}

View File

@ -0,0 +1,6 @@
package net.corda.nodeapi.internal.protonwrapper.netty
enum class ConnectionResult {
NO_ERROR,
HANDSHAKE_FAILURE
}

View File

@ -24,7 +24,7 @@ import net.corda.nodeapi.internal.namedThreadPoolExecutor
import net.corda.nodeapi.internal.revocation.CordaRevocationChecker
import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.DERIA5String
import org.bouncycastle.asn1.ASN1IA5String
import org.bouncycastle.asn1.DEROctetString
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.CRLDistPoint
@ -96,7 +96,7 @@ fun X509Certificate.distributionPoints(): Map<URI, List<X500Principal>?> {
}
for (generalName in GeneralNames.getInstance(distributionPointName.name).names) {
if (generalName.tagNo == GeneralName.uniformResourceIdentifier) {
val uri = URI(DERIA5String.getInstance(generalName.name).string)
val uri = URI(ASN1IA5String.getInstance(generalName.name).string)
dpMap[uri] = issuerNames
}
}

View File

@ -1,17 +1,23 @@
package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.DefaultSerializer
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.Registration
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.FieldSerializer
import com.esotericsoftware.kryo.util.DefaultClassResolver
import com.esotericsoftware.kryo.util.Util
import net.corda.core.internal.kotlinObjectInstance
import net.corda.core.internal.utilities.PrivateInterner
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.core.serialization.internal.AttachmentsClassLoader
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.utilities.contextLogger
import net.corda.serialization.internal.MutableClassWhitelist
import net.corda.serialization.internal.TransientClassWhiteList
import net.corda.serialization.internal.amqp.hasCordaSerializable
@ -19,8 +25,11 @@ import java.io.PrintWriter
import java.lang.reflect.Modifier.isAbstract
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Paths
import java.nio.file.StandardOpenOption.*
import java.util.*
import java.nio.file.StandardOpenOption.APPEND
import java.nio.file.StandardOpenOption.CREATE
import java.nio.file.StandardOpenOption.WRITE
import java.util.ArrayList
import java.util.Collections
/**
* Corda specific class resolver which enables extra customisation for the purposes of serialization using Kryo
@ -86,7 +95,7 @@ class CordaClassResolver(serializationContext: CheckpointSerializationContext) :
kotlin.jvm.internal.Lambda::class.java.isAssignableFrom(targetType) -> // Kotlin lambdas extend this class and any captured variables are stored in synthetic fields
FieldSerializer<Any>(kryo, targetType).apply { setIgnoreSyntheticFields(false) }
Throwable::class.java.isAssignableFrom(targetType) -> ThrowableSerializer(kryo, targetType)
else -> kryo.getDefaultSerializer(targetType)
else -> maybeWrapForInterning(kryo.getDefaultSerializer(targetType), targetType)
}
return register(Registration(targetType, serializer, NAME.toInt()))
} finally {
@ -94,6 +103,11 @@ class CordaClassResolver(serializationContext: CheckpointSerializationContext) :
}
}
private fun maybeWrapForInterning(serializer: Serializer<Any>, targetType: Class<*>): Serializer<Any> {
val interner = PrivateInterner.findFor(targetType)
return if (interner != null) InterningSerializer(serializer, interner) else serializer
}
override fun writeName(output: Output, type: Class<*>, registration: Registration) {
super.writeName(output, registration.type ?: type, registration)
}
@ -104,6 +118,11 @@ class CordaClassResolver(serializationContext: CheckpointSerializationContext) :
override fun write(kryo: Kryo, output: Output, obj: Any) = Unit
}
private class InterningSerializer(private val delegate: Serializer<Any>, private val interner: PrivateInterner<Any>) : Serializer<Any>() {
override fun read(kryo: Kryo, input: Input, type: Class<Any>): Any = interner.intern(delegate.read(kryo, input, type))
override fun write(kryo: Kryo, output: Output, obj: Any) = delegate.write(kryo, output, obj)
}
// We don't allow the annotation for classes in attachments for now. The class will be on the main classpath if we have the CorDapp installed.
// We also do not allow extension of KryoSerializable for annotated classes, or combination with @DefaultSerializer for custom serialisation.
// TODO: Later we can support annotations on attachment classes and spin up a proxy via bytecode that we know is harmless.

View File

@ -59,9 +59,8 @@ import java.security.PrivateKey
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.util.Arrays
import java.util.BitSet
import java.util.ServiceLoader
import java.util.*
import kotlin.collections.ArrayList
object DefaultKryoCustomizer {
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
@ -233,7 +232,7 @@ object DefaultKryoCustomizer {
@Suppress("UNCHECKED_CAST")
override fun read(kryo: Kryo, input: Input, type: Class<ContractAttachment>): ContractAttachment {
if (kryo.serializationContext() != null) {
val attachmentHash = SecureHash.SHA256(input.readBytes(32))
val attachmentHash = SecureHash.createSHA256(input.readBytes(32))
val contract = input.readString()
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
val uploader = input.readString()

View File

@ -9,22 +9,38 @@ import com.google.common.primitives.Ints
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.*
import net.corda.core.contracts.SignatureAttachmentConstraint
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sha256
import net.corda.core.crypto.sign
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.FetchDataFlow
import net.corda.core.serialization.*
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.EncodingWhitelist
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.checkpointDeserialize
import net.corda.core.serialization.internal.checkpointSerialize
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.sequence
import net.corda.serialization.internal.*
import net.corda.coretesting.internal.rigorousMock
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.CheckpointSerializationContextImpl
import net.corda.serialization.internal.CordaSerializationEncoding
import net.corda.serialization.internal.encodingNotPermittedFormat
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule
import net.corda.coretesting.internal.rigorousMock
import org.apache.commons.lang3.SystemUtils
import org.assertj.core.api.Assertions.*
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.assertj.core.api.Assertions.catchThrowable
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Before
@ -36,9 +52,13 @@ import org.junit.runners.Parameterized.Parameters
import org.slf4j.LoggerFactory
import java.io.InputStream
import java.time.Instant
import java.util.*
import kotlin.collections.ArrayList
import kotlin.test.*
import java.util.Collections
import java.util.Random
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertSame
import kotlin.test.assertTrue
@RunWith(Parameterized::class)
class KryoTests(private val compression: CordaSerializationEncoding?) {
@ -129,6 +149,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val deserialisedSignature = deserialisedKeyPair.sign(bitsToSign)
deserialisedSignature.verify(bitsToSign)
assertThatThrownBy { deserialisedSignature.verify(wrongBits) }
assertSame(keyPair.public, deserialisedKeyPair.public)
}
@Test(timeout=300_000)
@ -186,6 +207,24 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val serializedMetaData = meta.checkpointSerialize(context).bytes
val meta2 = serializedMetaData.checkpointDeserialize<SignableData>(context)
assertEquals(meta2, meta)
assertSame(meta.txId, meta2.txId)
}
@Test(timeout = 300_000)
fun `serialize - deserialize internables`() {
val list: List<Any> = listOf(
SecureHash.randomSHA256(),
CordaX500Name.parse("O=bank A, L=New York, C=DE, OU=Org Unit, CN=Service Name"),
Party.create(CordaX500Name.parse("O=bank A, L=New York, C=DE, OU=Org Unit, CN=Service Name"), Crypto.generateKeyPair().public),
AnonymousParty.create(Crypto.generateKeyPair().public),
SignatureAttachmentConstraint.create(Crypto.generateKeyPair().public)
)
val serializedList = list.checkpointSerialize(context).bytes
val list2 = serializedList.checkpointDeserialize<List<Any>>(context)
list.zip(list2).forEach { (original, deserialized) ->
assertSame(original, deserialized, "${original.javaClass} not interned")
}
}
@Test(timeout = 300_000)
@ -194,7 +233,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val logger = LoggerFactory.getLogger("aName")
val logger2 = logger.checkpointSerialize(storageContext).checkpointDeserialize(storageContext)
assertEquals(logger.name, logger2.name)
assertTrue(logger === logger2)
assertSame(logger, logger2)
}
@CordaSerializable

View File

@ -100,6 +100,7 @@ dependencies {
compile project(':common-configuration-parsing')
compile project(':common-logging')
implementation "io.opentelemetry:opentelemetry-api:${open_telemetry_version}"
// Backwards compatibility goo: Apps expect confidential-identities to be loaded by default.
// We could eventually gate this on a target-version check.
compile project(':confidential-identities')
@ -309,7 +310,8 @@ quasar {
"org.w3c.**",
"org.xml**",
"org.yaml**",
"rx**")
"rx**",
"io.opentelemetry.**")
}
jar {

View File

@ -96,7 +96,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: [
applicationVersion = corda_release_version
applicationId = "net.corda.node.Corda"
// See experimental/quasar-hook/README.md for how to generate.
def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.corda.djvm**;djvm**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**)"
def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.corda.djvm**;djvm**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)"
def quasarClassLoaderExclusion = "l(net.corda.djvm.**;net.corda.core.serialization.internal.**)"
javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"]
systemProperties['visualvm.display.name'] = 'Corda'

View File

@ -14,6 +14,7 @@ import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
import net.corda.nodeapi.internal.protonwrapper.netty.ConnectionResult
import net.corda.nodeapi.internal.protonwrapper.netty.RevocationConfig
import net.corda.nodeapi.internal.protonwrapper.netty.RevocationConfigImpl
import net.corda.nodeapi.internal.protonwrapper.netty.keyManagerFactory
@ -33,6 +34,7 @@ import org.junit.runners.Parameterized
import java.time.Duration
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.TrustManagerFactory
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@ -219,7 +221,7 @@ class AMQPClientSslErrorsTest(@Suppress("unused") private val iteration: Int) {
val clientConnect = clientConnected.get()
assertFalse(clientConnect.connected)
// Not a badCert, but a timeout during handshake
assertFalse(clientConnect.badCert)
assertEquals(ConnectionResult.NO_ERROR, clientConnect.connectionResult)
}
}
assertFalse(serverThread.isActive)

View File

@ -26,6 +26,7 @@ import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPClient
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPConfiguration
import net.corda.nodeapi.internal.protonwrapper.netty.AMQPServer
import net.corda.nodeapi.internal.protonwrapper.netty.init
import net.corda.nodeapi.internal.protonwrapper.netty.keyManagerFactory
import net.corda.nodeapi.internal.protonwrapper.netty.toRevocationConfig
import net.corda.nodeapi.internal.protonwrapper.netty.trustManagerFactory
@ -38,6 +39,7 @@ import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.apache.activemq.artemis.api.core.QueueConfiguration
import org.apache.activemq.artemis.api.core.RoutingType
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Assert.assertArrayEquals
import org.junit.Rule
@ -45,11 +47,14 @@ import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLException
import javax.net.ssl.SSLHandshakeException
import javax.net.ssl.SSLParameters
import javax.net.ssl.SSLServerSocket
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManagerFactory
import kotlin.concurrent.thread
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@ -208,6 +213,103 @@ class ProtonWrapperTests {
assertTrue(done)
}
@Suppress("TooGenericExceptionCaught") // Too generic exception thrown!
@Test(timeout=300_000)
fun `AMPQClient that fails to handshake with a server will retry the server`() {
/*
This test has been modelled on `Test AMQP Client with invalid root certificate`, above.
The aim is to set up a server with an invalid root cert so that the TLS handshake will fail.
The test allows the AMQPClient to retry the connection (which it should do).
*/
val certificatesDirectory = temporaryFolder.root.toPath()
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "serverstorepass")
val sslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = "serverstorepass")
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
// Generate server cert and private key and populate another keystore suitable for SSL
signingCertificateStore.get(true).also { it.installDevNodeCaCertPath(ALICE_NAME, rootCa.certificate, intermediateCa) }
sslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(ALICE_NAME, rootCa.certificate, intermediateCa) }
sslConfig.createTrustStore(rootCa.certificate)
val keyStore = sslConfig.keyStore.get()
val trustStore = sslConfig.trustStore.get()
val context = SSLContext.getInstance("TLS")
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore)
val keyManagers = keyManagerFactory.keyManagers
val trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustMgrFactory.init(trustStore.value.internal)
val trustManagers = trustMgrFactory.trustManagers
context.init(keyManagers, trustManagers, newSecureRandom())
val serverSocketFactory = context.serverSocketFactory
val serverSocket = serverSocketFactory.createServerSocket(serverPort) as SSLServerSocket
val serverParams = SSLParameters(ArtemisTcpTransport.CIPHER_SUITES.toTypedArray(),
arrayOf("TLSv1.2"))
serverParams.wantClientAuth = true
serverParams.needClientAuth = true
serverParams.endpointIdentificationAlgorithm = null // Reconfirm default no server name indication, use our own validator.
serverSocket.sslParameters = serverParams
serverSocket.useClientMode = false
var done = false
var handshakeErrorCount = 0
//
// This is the thread that acts as the server-side endpoint for the AMQPClient to connect to.
//
val serverThread = thread {
//
// The server thread will keep making itself available for SSL connections until
// the 'done' flag is set by the client thread, later on.
//
while (!done) {
try {
val sslServerSocket = serverSocket.accept() as SSLSocket
sslServerSocket.addHandshakeCompletedListener {
done = true
}
sslServerSocket.startHandshake()
} catch (ex: SSLException) {
++handshakeErrorCount
} catch (e: Throwable) {
println(e)
}
}
}
//
// Create the AMQPClient but only specify one server endpoint to connect to.
//
val amqpClient = createClient(serverAddressList = listOf(NetworkHostAndPort("localhost", serverPort)))
amqpClient.use {
amqpClient.start()
//
// Waiting for the number of handshake errors to get to at least 2.
// This happens when the AMQPClient has made it's first retry attempt, which is
// what this test is interested in.
//
while (handshakeErrorCount < 2) {
Thread.sleep(2)
}
done = true
}
serverThread.join(1000)
//
// check that there was at least one retry i.e. > 1 handshake error.
//
Assertions.assertThat(handshakeErrorCount > 1).isTrue()
serverSocket.close()
assertTrue(done)
}
@Test(timeout=300_000)
fun `Client Failover for multiple IP`() {
@ -451,7 +553,11 @@ class ProtonWrapperTests {
return Pair(server, client)
}
private fun createClient(maxMessageSize: Int = MAX_MESSAGE_SIZE): AMQPClient {
private fun createClient(maxMessageSize: Int = MAX_MESSAGE_SIZE,
serverAddressList: List<NetworkHostAndPort> = listOf(
NetworkHostAndPort("localhost", serverPort),
NetworkHostAndPort("localhost", serverPort2),
NetworkHostAndPort("localhost", artemisPort))): AMQPClient {
val baseDirectory = temporaryFolder.root.toPath() / "client"
val certificatesDirectory = baseDirectory / "certificates"
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory)
@ -475,9 +581,7 @@ class ProtonWrapperTests {
override val maxMessageSize: Int = maxMessageSize
}
return AMQPClient(
listOf(NetworkHostAndPort("localhost", serverPort),
NetworkHostAndPort("localhost", serverPort2),
NetworkHostAndPort("localhost", artemisPort)),
serverAddressList,
setOf(ALICE_NAME, CHARLIE_NAME),
amqpConfig)
}

View File

@ -16,6 +16,7 @@ import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow
import net.corda.core.node.AppServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.ServiceLifecycleEvent
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -854,6 +855,13 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
init {
if (includeRawUpdates) {
services.register {
processEvent(it)
}
}
}
private fun processEvent(event : ServiceLifecycleEvent) {
if (event == ServiceLifecycleEvent.STATE_MACHINE_STARTED) {
services.vaultService.rawUpdates.subscribe {
if (insertionType == InsertionType.ENTITY_MANAGER) {
services.withEntityManager {
@ -871,7 +879,6 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
}
}
}
}
private fun Connection.insert(entity: CustomTableEntity) {
prepareStatement("INSERT INTO $TABLE_NAME VALUES (?, ?, ?)").apply {

View File

@ -0,0 +1,42 @@
package net.corda.node.jmx
import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.nodeapi.internal.NodeStatus
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.JmxPolicy
import net.corda.testing.driver.driver
import org.junit.Test
import java.net.HttpURLConnection
import java.net.HttpURLConnection.HTTP_OK
import java.net.URL
import java.util.stream.Collectors
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class NodeStatusTest {
@Test(timeout=300_000)
fun `node status is published via JMX`() {
driver(DriverParameters(notarySpecs = emptyList(), jmxPolicy = JmxPolicy.defaultEnabled())) {
val jmxAddress = startNode().get().jmxAddress.toString()
val nodeStatusURL = URL("http://$jmxAddress/jolokia/read/net.corda:name=Status,type=Node")
val jmxInfo = with(nodeStatusURL.openConnection() as HttpURLConnection) {
requestMethod = "GET"
inputStream.bufferedReader().use {
it.lines().collect(Collectors.toList()).joinToString()
}
}
assertTrue {
jmxInfo.isNotEmpty()
}
val jsonTree = ObjectMapper().readTree(jmxInfo)
val httpStatus = jsonTree.get("status").asInt()
val nodeStatus = jsonTree.get("value").get("Value").asText()
assertEquals(httpStatus, HTTP_OK)
assertEquals(nodeStatus, NodeStatus.STARTED.toString())
}
}
}

View File

@ -0,0 +1,28 @@
package net.corda.node.jmx
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.JmxPolicy
import net.corda.testing.driver.driver
import org.junit.Test
import java.net.HttpURLConnection
import java.net.URL
import kotlin.test.assertTrue
class PublishTest {
@Test(timeout=300_000)
fun `node publishes node information via JMX when configured to do so`() {
driver(DriverParameters(notarySpecs = emptyList(), jmxPolicy = JmxPolicy.defaultEnabled())) {
val jmxAddress = startNode().get().jmxAddress.toString()
val nodeStatusURL = URL("http://$jmxAddress/jolokia/read/net.corda:*")
val httpResponse = with(nodeStatusURL.openConnection() as HttpURLConnection) {
requestMethod = "GET"
responseCode
}
assertTrue {
httpResponse == HttpURLConnection.HTTP_OK
}
}
}
}

View File

@ -51,6 +51,7 @@ class CordaServiceLifecycleFatalTests {
object FailingObserver : ServiceLifecycleObserver {
override fun onServiceLifecycleEvent(event: ServiceLifecycleEvent) {
if (event == ServiceLifecycleEvent.STATE_MACHINE_STARTED) {
val tmpFile = File(System.getProperty(tempFilePropertyName))
tmpFile.appendText("\n" + readyToThrowMarker)
eventually(duration = 30.seconds) {
@ -60,6 +61,7 @@ class CordaServiceLifecycleFatalTests {
}
}
}
}
@Test(timeout=300_000)
fun `JVM terminates on critical failure`() {

View File

@ -3,18 +3,23 @@ package net.corda.node.services
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.StartableByService
import net.corda.core.messaging.startFlow
import net.corda.core.node.AppServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.ServiceLifecycleEvent
import net.corda.core.node.services.ServiceLifecycleEvent.BEFORE_STATE_MACHINE_START
import net.corda.core.node.services.ServiceLifecycleEvent.STATE_MACHINE_STARTED
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.getOrThrow
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.InProcess
import net.corda.testing.driver.driver
import net.corda.testing.node.internal.enclosedCordapp
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
class CordaServiceLifecycleTests {
@ -22,23 +27,70 @@ class CordaServiceLifecycleTests {
private companion object {
const val TEST_PHRASE = "testPhrase"
// the number of times to register a service callback
private var numServiceCallbacks = 0
// the set of events a test wants to capture
private var eventsToBeCaptured: MutableSet<ServiceLifecycleEvent> = mutableSetOf()
// the events that were actually captured in a test
private val eventsCaptured: MutableList<ServiceLifecycleEvent> = mutableListOf()
}
@Before
fun setup() {
numServiceCallbacks = 1
eventsCaptured.clear()
eventsToBeCaptured = setOf(BEFORE_STATE_MACHINE_START, STATE_MACHINE_STARTED).toMutableSet()
}
@Test(timeout=300_000)
fun `corda service receives events`() {
eventsCaptured.clear()
val result = driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = listOf(enclosedCordapp()),
notarySpecs = emptyList())) {
val node = startNode(providedName = ALICE_NAME).getOrThrow()
node.rpc.startFlow(::ComputeTextLengthThroughCordaService, TEST_PHRASE).returnValue.getOrThrow()
}
val expectedEventsAndTheOrderTheyOccurIn = listOf(BEFORE_STATE_MACHINE_START, STATE_MACHINE_STARTED)
assertEquals(TEST_PHRASE.length, result)
assertEquals(1, eventsCaptured.size)
assertEquals(listOf(STATE_MACHINE_STARTED), eventsCaptured)
assertEquals(numServiceCallbacks * 2, eventsCaptured.size)
assertEquals(expectedEventsAndTheOrderTheyOccurIn, eventsCaptured)
}
@Test(timeout=300_000)
fun `corda service receives BEFORE_STATE_MACHINE_START before the state machine is started`() {
testStateMachineManagerStatusWhenServiceEventOccurs(
event = BEFORE_STATE_MACHINE_START,
expectedResult = TestSmmStateService.STATE_MACHINE_MANAGER_WAS_NOT_STARTED
)
}
@Test(timeout=300_000)
fun `corda service receives STATE_MACHINE_STARTED after the state machine is started`() {
testStateMachineManagerStatusWhenServiceEventOccurs(
event = STATE_MACHINE_STARTED,
expectedResult = TestSmmStateService.STATE_MACHINE_MANAGER_WAS_STARTED
)
}
/**
* Commonised
*/
private fun testStateMachineManagerStatusWhenServiceEventOccurs(event: ServiceLifecycleEvent, expectedResult : Int) {
val result = driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = listOf(enclosedCordapp()),
notarySpecs = emptyList())) {
val node = startNode(providedName = ALICE_NAME).getOrThrow()
if (node is InProcess) { // assuming the node-handle is always one of these
val svc = node.services.cordaService(TestSmmStateService::class.java)
svc.getSmmStartedForEvent(event)
} else {
TestSmmStateService.STATE_MACHINE_MANAGER_UNKNOWN_STATUS
}
}
assertEquals(expectedResult, result)
}
@StartableByRPC
@StartableByService
class ComputeTextLengthThroughCordaService(private val text: String) : FlowLogic<Int>() {
@Suspendable
override fun call(): Int {
@ -52,18 +104,15 @@ class CordaServiceLifecycleTests {
class TextLengthComputingService(services: AppServiceHub) : SingletonSerializeAsToken() {
init {
for (n in 1..numServiceCallbacks) {
services.register { addEvent(it) }
}
}
private fun addEvent(event: ServiceLifecycleEvent) {
when (event) {
STATE_MACHINE_STARTED -> {
if (event in eventsToBeCaptured) {
eventsCaptured.add(event)
}
else -> {
eventsCaptured.add(event)
}
}
}
fun computeLength(text: String): Int {
@ -71,4 +120,42 @@ class CordaServiceLifecycleTests {
return text.length
}
}
/**
* Service that checks the State Machine Manager state (started, not started) when service events are received.
*/
@CordaService
class TestSmmStateService(private val services: AppServiceHub) : SingletonSerializeAsToken() {
companion object {
const val STATE_MACHINE_MANAGER_UNKNOWN_STATUS = -1
const val STATE_MACHINE_MANAGER_WAS_NOT_STARTED = 0
const val STATE_MACHINE_MANAGER_WAS_STARTED = 1
}
var smmStateAtEvent = mutableMapOf<ServiceLifecycleEvent, Int>()
init {
services.register { addEvent(it) }
}
private fun addEvent(event: ServiceLifecycleEvent) {
smmStateAtEvent[event] = checkSmmStarted()
}
private fun checkSmmStarted() : Int {
// try to start a flow; success == SMM started
try {
services.startFlow(ComputeTextLengthThroughCordaService(TEST_PHRASE)).returnValue.getOrThrow()
return STATE_MACHINE_MANAGER_WAS_STARTED
} catch (ex : UninitializedPropertyAccessException) {
return STATE_MACHINE_MANAGER_WAS_NOT_STARTED
}
}
/**
* Given an event, was the SMM started when the event was received?
*/
fun getSmmStartedForEvent(event: ServiceLifecycleEvent) : Int = smmStateAtEvent.getOrDefault(event, STATE_MACHINE_MANAGER_UNKNOWN_STATUS)
}
}

View File

@ -0,0 +1,133 @@
package net.corda.node.services
import net.corda.core.node.AppServiceHub
import net.corda.core.node.AppServiceHub.Companion.SERVICE_PRIORITY_HIGH
import net.corda.core.node.AppServiceHub.Companion.SERVICE_PRIORITY_LOW
import net.corda.core.node.AppServiceHub.Companion.SERVICE_PRIORITY_NORMAL
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.ServiceLifecycleEvent
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.getOrThrow
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import net.corda.testing.node.internal.enclosedCordapp
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
/**
* Test the priorities of Corda services when distributing/handlimg service events.
* Services register themselves with a priority - an integer whereby higher numbers == higher priority.
* There are a few pre-defined priorities provided by Corda:
* SERVICE_PRIORITY_HIGH
* SERVICE_PRIORITY_NORMAL
* SERVICE_PRIORITY_LOW
*
* but actually the priority can be ANY integer the Corda service desires.
*/
/**
* This base class commonizes code for the subsequent real test classes, further down.
*/
open class CordaServicePriorityTests {
companion object {
val eventsCaptured: MutableMap<ServiceLifecycleEvent, MutableList<String>> = mutableMapOf()
}
/**
* Services loaded by the node
*/
open class PriorityService(private val services: AppServiceHub, private val priority : Int, private val name : String) : SingletonSerializeAsToken() {
init {
services.register(priority = priority) { addEvent(it) }
}
private fun addEvent(event: ServiceLifecycleEvent) {
eventsCaptured.getOrPut(event) {
mutableListOf()
}.add(name)
}
}
@Before
fun startUp() {
eventsCaptured.clear()
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = listOf(enclosedCordapp()),
notarySpecs = emptyList())) {
startNode(providedName = ALICE_NAME).getOrThrow()
}
}
}
/**
* Test the priorities of Corda services when distributing/handlimg service events where services have different priorities.
*
* The expectation is that service events BEFORE_STATE_MACHINE_START and STATE_MACHINE_STARTED are delivered to Corda Services
* in priority order. That is, services registered with a higher priority value are sent the events first.
*/
class CordaServiceDifferentPriorityTests : CordaServicePriorityTests() {
@Test(timeout=300_000)
@Suppress("unused")
fun `startup service events are delivered to Corda Services in priority order`() {
// expect events to be delivered to these Corda Services in this order
val expectedCallList = listOf("John", "Paul", "George", "Ringo")
assertEquals(expectedCallList, eventsCaptured[ServiceLifecycleEvent.BEFORE_STATE_MACHINE_START]?.toList())
assertEquals(expectedCallList, eventsCaptured[ServiceLifecycleEvent.STATE_MACHINE_STARTED]?.toList())
}
/*
These are the services with different priorities.
*/
@CordaService
class John(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_HIGH, name = "John")
@CordaService
class Paul(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_HIGH - 1, name = "Paul")
@CordaService
class George(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_NORMAL, name = "George")
@CordaService
class Ringo(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_LOW, name = "Ringo")
}
/**
* Test the priorities of Corda services when distributing/handlimg service events where services have the same priorities.
*
* The expectation is that service events BEFORE_STATE_MACHINE_START and STATE_MACHINE_STARTED are delivered to all Corda Services
* but the order of delivery is not defined, and may differ from run to run.
*/
class CordaServiceSamePriorityTests : CordaServicePriorityTests() {
@Test(timeout=300_000)
@Suppress("unused")
fun `startup service events are delivered to all equal-priority services, the ordering is not fixed`() {
// expect the service events to be delivered to these Corda Services, but don;t care about the order.
val expectedCallList = listOf("Pete", "Roger", "John", "Keith")
assertEquals(expectedCallList.sorted(), eventsCaptured[ServiceLifecycleEvent.BEFORE_STATE_MACHINE_START]?.toList()?.sorted())
assertEquals(expectedCallList.sorted(), eventsCaptured[ServiceLifecycleEvent.STATE_MACHINE_STARTED]?.toList()?.sorted())
}
/*
These are the services with all the same priority.
*/
@CordaService
class Pete(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_NORMAL, name = "Pete")
@CordaService
class Roger(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_NORMAL, name = "Roger")
@CordaService
class John(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_NORMAL, name = "John")
@CordaService
class Keith(val services: AppServiceHub) : PriorityService(services, priority = SERVICE_PRIORITY_NORMAL, name = "Keith")
}

View File

@ -1,6 +1,7 @@
package net.corda.node.internal
import co.paralleluniverse.fibers.instrument.Retransform
import com.codahale.metrics.Gauge
import com.codahale.metrics.MetricRegistry
import com.google.common.collect.MutableClassToInstanceMap
import com.google.common.util.concurrent.MoreExecutors
@ -56,6 +57,11 @@ import net.corda.core.node.services.ContractUpgradeService
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.internal.telemetry.OpenTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.node.services.TelemetryService
import net.corda.core.node.services.TransactionVerifierService
import net.corda.core.node.services.diagnostics.DiagnosticsService
import net.corda.core.schemas.MappedSchema
@ -148,6 +154,7 @@ import net.corda.node.utilities.BindableNamedCacheFactory
import net.corda.node.utilities.NamedThreadFactory
import net.corda.node.utilities.NotaryLoader
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.NodeStatus
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.nodeapi.internal.cryptoservice.CryptoService
@ -249,6 +256,16 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
val rotatedKeys = makeRotatedKeysService(configuration).tokenize()
val cordappLoader: CordappLoader = makeCordappLoader(configuration, versionInfo, rotatedKeys).closeOnStop(false)
val telemetryService: TelemetryServiceImpl = TelemetryServiceImpl().also {
val openTelemetryComponent = OpenTelemetryComponent(configuration.myLegalName.toString(), configuration.telemetry.spanStartEndEventsEnabled, configuration.telemetry.copyBaggageToTags)
if (configuration.telemetry.openTelemetryEnabled && openTelemetryComponent.isEnabled()) {
it.addTelemetryComponent(openTelemetryComponent)
}
if (configuration.telemetry.simpleLogTelemetryEnabled) {
it.addTelemetryComponent(SimpleLogTelemetryComponent())
}
runOnStop += { it.shutdownTelemetry() }
}.tokenize()
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas).tokenize()
val identityService = PersistentIdentityService(cacheFactory).tokenize()
val database: CordaPersistence = createCordaPersistence(
@ -336,6 +353,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
private val schedulerService = makeNodeSchedulerService()
private val cordappServices = MutableClassToInstanceMap.create<SerializeAsToken>()
private val cordappTelemetryComponents = MutableClassToInstanceMap.create<TelemetryComponent>()
private val shutdownExecutor = Executors.newSingleThreadExecutor(DefaultThreadFactory("Shutdown"))
protected abstract val transactionVerifierWorkerCount: Int
@ -384,6 +402,9 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
protected val keyStoreHandler = KeyStoreHandler(configuration, cryptoService)
@Volatile
private var nodeStatus = NodeStatus.WAITING_TO_START
private fun <T : Any> T.tokenize(): T {
tokenizableServices?.add(this as? SerializeAsToken ?:
throw IllegalStateException("${this::class.java} is expected to be extending from SerializeAsToken"))
@ -525,6 +546,12 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
Node.printBasicNodeInfo("CorDapp schemas synchronised")
}
private fun setNodeStatus(st : NodeStatus) {
log.info("Node status update: [$nodeStatus] -> [$st]")
nodeStatus = st
}
@Suppress("ComplexMethod")
open fun start(): S {
check(started == null) { "Node has already been started" }
@ -534,9 +561,12 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeNodeStart(nodeServicesContext))
log.info("Node starting up ...")
setNodeStatus(NodeStatus.STARTING)
initialiseJolokia()
monitoringService.metrics.register(MetricRegistry.name("Node", "Status"), Gauge { nodeStatus })
val trustRoots = initKeyStores()
initialiseJolokia()
schemaService.mappedSchemasWarnings().forEach {
val warning = it.toWarning()
@ -616,6 +646,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
// the identity key. But the infrastructure to make that easy isn't here yet.
keyManagementService.start(keyStoreHandler.signingKeys.map { it.key to it.alias })
installTelemetryComponents()
installCordaServices()
notaryService = maybeStartNotaryService(keyStoreHandler.notaryIdentity)
contractUpgradeService.start()
@ -626,6 +657,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
tokenizableServices = null
verifyCheckpointsCompatible(frozenTokenizableServices)
/* Note the .get() at the end of the distributeEvent call, below.
This will block until all Corda Services have returned from processing the event, allowing a service to prevent the
state machine manager from starting (just below this) until the service is ready.
*/
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeStateMachineStart(nodeServicesContext)).get()
val callback = smm.start(frozenTokenizableServices)
val smmStartedFuture = rootFuture.map { callback() }
// Shut down the SMM so no Fibers are scheduled.
@ -659,6 +695,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
log.warn("Not distributing events as NetworkMap is not ready")
}
}
setNodeStatus(NodeStatus.STARTED)
return resultingNodeInfo
}
@ -961,6 +998,62 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
return service
}
private class TelemetryComponentInstantiationException(cause: Throwable?) : CordaException("Service Instantiation Error", cause)
@Suppress("ThrowsCount", "ComplexMethod", "NestedBlockDepth")
private fun installTelemetryComponents() {
val loadedTelemetryComponents: List<Class<out TelemetryComponent>> = cordappLoader.cordapps.flatMap { it.telemetryComponents }.filterNot {
it.name == OpenTelemetryComponent::class.java.name ||
it.name == SimpleLogTelemetryComponent::class.java.name }
// This sets the Cordapp classloader on the contextClassLoader of the current thread, prior to initializing telemetry components
// Needed because of bug CORDA-2653 - some telemetry components can utilise third-party libraries that require access to
// the Thread context class loader. (Same as installCordaServices).
val oldContextClassLoader: ClassLoader? = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = cordappLoader.appClassLoader
loadedTelemetryComponents.forEach {
try {
installTelemetryComponent(it)
} catch (e: NoSuchMethodException) {
log.error("Missing no arg ctor for ${it.name}")
throw e
} catch (e: TelemetryComponentInstantiationException) {
if (e.cause != null) {
log.error("Corda telemetry component ${it.name} failed to instantiate. Reason was: ${e.cause?.rootMessage}", e.cause)
} else {
log.error("Corda telemetry component ${it.name} failed to instantiate", e)
}
throw e
} catch (e: Exception) {
log.error("Unable to install Corda telemetry component ${it.name}", e)
throw e
}
}
} finally {
Thread.currentThread().contextClassLoader = oldContextClassLoader
}
}
private fun <T : TelemetryComponent> installTelemetryComponent(telemetryComponentClass: Class<T>) {
val telemetryComponent = try {
val extendedTelemetryComponentConstructor = telemetryComponentClass.getDeclaredConstructor().apply { isAccessible = true }
val telemetryComponent = extendedTelemetryComponentConstructor.newInstance()
telemetryComponent
} catch (e: InvocationTargetException) {
throw TelemetryComponentInstantiationException(e.cause)
}
cordappTelemetryComponents.putInstance(telemetryComponentClass, telemetryComponent)
if (telemetryComponent.isEnabled()) {
telemetryService.addTelemetryComponent(telemetryComponent)
log.info("Installed ${telemetryComponentClass.name} Telemetry component")
}
else {
log.info("${telemetryComponentClass.name} not enabled so not installing")
}
}
private fun registerCordappFlows() {
cordappLoader.cordapps.forEach { cordapp ->
cordapp.initiatedFlows.groupBy { it.requireAnnotation<InitiatedBy>().value.java }.forEach { initiator, responders ->
@ -1054,11 +1147,13 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
// the identity key. But the infrastructure to make that easy isn't here yet.
return BasicHSMKeyManagementService(cacheFactory, identityService, database, cryptoService)
return BasicHSMKeyManagementService(cacheFactory, identityService, database, cryptoService, telemetryService)
}
open fun stop() {
setNodeStatus(NodeStatus.STOPPING)
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.StateMachineStopped(nodeServicesContext))
nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.BeforeNodeStop(nodeServicesContext))
@ -1134,6 +1229,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
override val externalOperationExecutor: ExecutorService get() = this@AbstractNode.externalOperationExecutor
override val notaryService: NotaryService? get() = this@AbstractNode.notaryService
override val rotatedKeys: RotatedKeys get() = this@AbstractNode.rotatedKeys
override val telemetryService: TelemetryService get() = this@AbstractNode.telemetryService
private lateinit var _myInfo: NodeInfo
override val myInfo: NodeInfo get() = _myInfo
@ -1155,6 +1251,11 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
}
override fun <T : TelemetryComponent> cordaTelemetryComponent(type: Class<T>): T {
return cordappTelemetryComponents.getInstance(type)
?: throw IllegalArgumentException("Corda telemetry component ${type.name} does not exist")
}
override fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? {
return flowManager.getFlowFactoryForInitiatingFlow(initiatingFlowClass)
}

View File

@ -45,6 +45,10 @@ internal class AppServiceHubImpl<T : SerializeAsToken>(private val serviceHub: S
observer.onServiceLifecycleEvent(ServiceLifecycleEvent.STATE_MACHINE_STARTED)
reportSuccess(nodeLifecycleEvent)
}
is NodeLifecycleEvent.BeforeStateMachineStart<*> -> Try.on {
observer.onServiceLifecycleEvent(ServiceLifecycleEvent.BEFORE_STATE_MACHINE_START)
reportSuccess(nodeLifecycleEvent)
}
else -> super.update(nodeLifecycleEvent)
}
}

View File

@ -582,14 +582,12 @@ open class Node(configuration: NodeConfiguration,
}
override fun start(): NodeInfo {
registerJmxReporter(services.monitoringService.metrics)
registerDefaultExceptionHandler()
initialiseSerialization()
val nodeInfo: NodeInfo = super.start()
nodeReadyFuture.thenMatch({
serverThread.execute {
registerJmxReporter(services.monitoringService.metrics)
_startupComplete.set(Unit)
}
},

View File

@ -19,6 +19,7 @@ import net.corda.core.internal.cordapp.get
import net.corda.core.internal.notary.NotaryService
import net.corda.core.internal.notary.SinglePartyNotaryService
import net.corda.core.node.services.CordaService
import net.corda.core.internal.telemetry.TelemetryComponent
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.CheckpointCustomSerializer
import net.corda.core.serialization.SerializationCustomSerializer
@ -188,6 +189,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
findServiceFlows(this),
findSchedulableFlows(this),
findServices(this),
findTelemetryComponents(this),
findWhitelists(url),
findSerializers(this),
findCheckpointSerializers(this),
@ -285,6 +287,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
return scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class)
}
private fun findTelemetryComponents(scanResult: RestrictedScanResult): List<Class<out TelemetryComponent>> {
return scanResult.getClassesImplementing(TelemetryComponent::class)
}
private fun findInitiatedFlows(scanResult: RestrictedScanResult): List<Class<out FlowLogic<*>>> {
return scanResult.getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class)
}
@ -418,6 +424,15 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
.map { it.kotlin.objectOrNewInstance() }
}
fun <T : Any> getClassesImplementing(type: KClass<T>): List<Class<out T>> {
return scanResult
.getClassesImplementing(type.java.name)
.filter { it.name.startsWith(qualifiedNamePrefix) }
.mapNotNull {
loadClass(it.name, type) }
.filterNot { it.isAbstractClass }
}
fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> {
return scanResult
.getClassesWithAnnotation(annotation.java.name)

View File

@ -30,6 +30,7 @@ internal object VirtualCordapp {
serviceFlows = listOf(),
schedulableFlows = listOf(),
services = listOf(),
telemetryComponents = listOf(),
serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(),
checkpointCustomSerializers = listOf(),
@ -54,6 +55,7 @@ internal object VirtualCordapp {
serviceFlows = listOf(),
schedulableFlows = listOf(),
services = listOf(),
telemetryComponents = listOf(),
serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(),
checkpointCustomSerializers = listOf(),
@ -79,6 +81,7 @@ internal object VirtualCordapp {
serviceFlows = listOf(),
schedulableFlows = listOf(),
services = listOf(),
telemetryComponents = listOf(),
serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(),
checkpointCustomSerializers = listOf(),
@ -103,6 +106,7 @@ internal object VirtualCordapp {
serviceFlows = listOf(),
schedulableFlows = listOf(),
services = listOf(),
telemetryComponents = listOf(),
serializationWhitelists = listOf(),
serializationCustomSerializers = listOf(),
checkpointCustomSerializers = listOf(),

View File

@ -43,6 +43,7 @@ interface NodeConfiguration : ConfigurationWithOptionsContainer {
val certificateChainCheckPolicies: List<CertChainPolicyConfig>
val verifierType: VerifierType
val flowTimeout: FlowTimeoutConfiguration
val telemetry: TelemetryConfiguration
val notary: NotaryConfig?
val additionalNodeInfoPollingFrequencyMsec: Long
val p2pAddress: NetworkHostAndPort
@ -234,6 +235,13 @@ data class FlowTimeoutConfiguration(
*/
data class RotatedCorDappSignerKeyConfiguration(val rotatedKeys: List<String>)
data class TelemetryConfiguration(
val openTelemetryEnabled: Boolean,
val simpleLogTelemetryEnabled: Boolean,
val spanStartEndEventsEnabled: Boolean,
val copyBaggageToTags: Boolean
)
internal typealias Valid<TARGET> = Validated<TARGET, Configuration.Validation.Error>
fun Config.parseAsNodeConfiguration(options: Configuration.Options = Configuration.Options(strict = true)): Valid<NodeConfiguration> = V1NodeConfigurationSpec.parse(this, options)

View File

@ -42,6 +42,7 @@ data class NodeConfigurationImpl(
override val security: SecurityConfiguration? = Defaults.security,
override val verifierType: VerifierType,
override val flowTimeout: FlowTimeoutConfiguration,
override val telemetry: TelemetryConfiguration = Defaults.telemetry,
override val p2pAddress: NetworkHostAndPort,
override val additionalP2PAddresses: List<NetworkHostAndPort> = Defaults.additionalP2PAddresses,
private val rpcAddress: NetworkHostAndPort? = Defaults.rpcAddress,
@ -135,6 +136,7 @@ data class NodeConfigurationImpl(
fun database(devMode: Boolean) = DatabaseConfig(
exportHibernateJMXStatistics = devMode
)
val telemetry = TelemetryConfiguration(openTelemetryEnabled = true, simpleLogTelemetryEnabled = false, spanStartEndEventsEnabled = false, copyBaggageToTags = false)
}
companion object {

View File

@ -31,6 +31,7 @@ import net.corda.node.services.config.PasswordEncryption
import net.corda.node.services.config.RotatedCorDappSignerKeyConfiguration
import net.corda.node.services.config.SecurityConfiguration
import net.corda.node.services.config.SecurityConfiguration.AuthService.Companion.defaultAuthServiceId
import net.corda.node.services.config.TelemetryConfiguration
import net.corda.node.services.config.Valid
import net.corda.node.services.config.schema.parsers.attempt
import net.corda.node.services.config.schema.parsers.badValue
@ -233,6 +234,18 @@ internal object RotatedSignerKeySpec : Configuration.Specification<RotatedCorDap
}
}
internal object TelemetryConfigurationSpec : Configuration.Specification<TelemetryConfiguration>("TelemetryConfiguration") {
private val openTelemetryEnabled by boolean()
private val simpleLogTelemetryEnabled by boolean()
private val spanStartEndEventsEnabled by boolean()
private val copyBaggageToTags by boolean()
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<TelemetryConfiguration> {
val config = configuration.withOptions(options)
return valid(TelemetryConfiguration(config[openTelemetryEnabled], config[simpleLogTelemetryEnabled], config[spanStartEndEventsEnabled], config[copyBaggageToTags]))
}
}
internal object NotaryConfigSpec : Configuration.Specification<NotaryConfig>("NotaryConfig") {
private val validating by boolean()
private val serviceLegalName by string().mapValid(::toCordaX500Name).optional()

View File

@ -25,6 +25,7 @@ internal object V1NodeConfigurationSpec : Configuration.Specification<NodeConfig
private val certificateChainCheckPolicies by nested(CertChainPolicyConfigSpec).list().optional().withDefaultValue(Defaults.certificateChainCheckPolicies)
private val verifierType by enum(VerifierType::class)
private val flowTimeout by nested(FlowTimeoutConfigurationSpec)
private val telemetry by nested(TelemetryConfigurationSpec).optional().withDefaultValue(Defaults.telemetry)
private val notary by nested(NotaryConfigSpec).optional()
private val additionalNodeInfoPollingFrequencyMsec by long().optional().withDefaultValue(Defaults.additionalNodeInfoPollingFrequencyMsec)
private val p2pAddress by string().mapValid(::toNetworkHostAndPort)
@ -96,6 +97,7 @@ internal object V1NodeConfigurationSpec : Configuration.Specification<NodeConfig
rpcUsers = config[rpcUsers],
verifierType = config[verifierType],
flowTimeout = config[flowTimeout],
telemetry = config[telemetry],
rpcSettings = config[rpcSettings],
messagingServerAddress = config[messagingServerAddress],
notary = config[notary],

View File

@ -2,6 +2,7 @@ package net.corda.node.services.keys
import net.corda.core.crypto.*
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
@ -31,7 +32,8 @@ class BasicHSMKeyManagementService(
cacheFactory: NamedCacheFactory,
override val identityService: PersistentIdentityService,
private val database: CordaPersistence,
private val cryptoService: SignOnlyCryptoService
private val cryptoService: SignOnlyCryptoService,
val telemetryService: TelemetryServiceImpl
) : SingletonSerializeAsToken(), KeyManagementServiceInternal {
@Entity
@ -134,6 +136,7 @@ class BasicHSMKeyManagementService(
}
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
telemetryService.span("${this::class.java.name}#sign") {
val signingPublicKey = getSigningPublicKey(publicKey)
return if (signingPublicKey in originalKeysMap) {
DigitalSignature.WithKey(signingPublicKey, cryptoService.sign(originalKeysMap[signingPublicKey]!!, bytes))
@ -142,6 +145,7 @@ class BasicHSMKeyManagementService(
keyPair.sign(bytes)
}
}
}
// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit
// signing to appropriately authorised contexts and initiating users.

View File

@ -12,10 +12,21 @@ import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.*
import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.FetchAttachmentsFlow
import net.corda.core.internal.JarSignatureCollector
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.P2P_UPLOADER
import net.corda.core.internal.RPC_UPLOADER
import net.corda.core.internal.TRUSTED_UPLOADERS
import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.internal.Version
import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION
import net.corda.core.internal.cordapp.CordappImpl.Companion.DEFAULT_CORDAPP_VERSION
import net.corda.core.internal.isUploaderTrusted
import net.corda.core.internal.readFully
import net.corda.core.internal.utilities.ZipBombDetector
import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentId
@ -23,7 +34,11 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.node.services.vault.Builder
import net.corda.core.node.services.vault.Sort
import net.corda.core.serialization.*
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationToken
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger
import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser
import net.corda.node.utilities.InfrequentlyMutatedCache
@ -46,7 +61,17 @@ import java.util.jar.JarEntry
import java.util.jar.JarInputStream
import java.util.stream.Stream
import javax.annotation.concurrent.ThreadSafe
import javax.persistence.*
import javax.persistence.CollectionTable
import javax.persistence.Column
import javax.persistence.ElementCollection
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.ForeignKey
import javax.persistence.Id
import javax.persistence.Index
import javax.persistence.JoinColumn
import javax.persistence.Lob
import javax.persistence.Table
/**
* Stores attachments using Hibernate to database.
@ -211,7 +236,7 @@ class NodeAttachmentService @JvmOverloads constructor(
private fun validate() {
if (counter.count != expectedSize.toLong()) return
val actual = SecureHash.SHA256(hash.asBytes())
val actual = SecureHash.createSHA256(hash.asBytes())
if (actual != expected)
throw HashMismatchException(expected, actual)
}

View File

@ -40,6 +40,7 @@ import net.corda.nodeapi.internal.persistence.contextDatabaseOrNull
import net.corda.nodeapi.internal.rpc.ObservableContextInterface
import net.corda.nodeapi.internal.rpc.ObservableSubscription
import net.corda.nodeapi.internal.serialization.amqp.RpcServerObservableSerializer
import net.corda.nodeapi.serializedTelemetry
import org.apache.activemq.artemis.api.core.Message
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
@ -63,6 +64,7 @@ import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.function.Predicate
import kotlin.concurrent.thread
private typealias ObservableSubscriptionMap = Cache<InvocationId, ObservableSubscription>
@ -359,6 +361,22 @@ class RPCServer(
}
private fun clientArtemisMessageHandler(artemisMessage: ClientMessage) {
/*
Local function for actually executing an RPC, either directly or through the thread pool
*/
fun executeRpc(context : RpcAuthContext, clientToServer : RPCApi.ClientToServer.RpcRequest, arguments : Try.Success<List<Any?>>, isQuickRpc : Boolean) {
if (isQuickRpc) {
val result = invokeRpc(context, clientToServer.methodName, arguments.value)
sendReply(clientToServer.replyId, clientToServer.clientAddress, result)
} else {
rpcExecutor!!.submit {
val result = invokeRpc(context, clientToServer.methodName, arguments.value)
sendReply(clientToServer.replyId, clientToServer.clientAddress, result)
}
}
}
lifeCycle.requireState(State.STARTED)
val clientToServer = RPCApi.ClientToServer.fromClientMessage(artemisMessage)
if (log.isDebugEnabled) {
@ -387,16 +405,45 @@ class RPCServer(
val arguments = Try.on {
clientToServer.serialisedArguments.deserialize<List<Any?>>(context = RPC_SERVER_CONTEXT)
}
log.debug("Received RPC request for [${clientToServer.methodName}]")
/*
The supplied method name may consist of <class>#<method>.
If just a method name is supplied then it is a call made via CordaRPCOps because a quirk of the
stored method names is that CordaRPCOps methods are stored without their class name.
The list of predicates below describes how to match quick RPC methods.
If at least one predicate returns true for the supplied method then it is treated as
a quick RPC.
*/
val quickRpcsList = listOf<Predicate<RPCApi.ClientToServer.RpcRequest>>(
// getProtocolVersion for any class
Predicate() { req ->
req.methodName.substringAfter(CLASS_METHOD_DIVIDER) == "getProtocolVersion"
},
// currentNodeTime for CordaRPCOps
Predicate() { req ->
req.methodName == "currentNodeTime"
}
// Add more predicates as and when needed
)
val isQuickRpc = if (quickRpcsList.any {
it.test(clientToServer)
}) {
log.debug("Handling [${clientToServer.methodName}] as a quick RPC")
true
} else {
false
}
val context: RpcAuthContext
when (arguments) {
is Try.Success -> {
context = artemisMessage.context(clientToServer.sessionId, arguments.value)
context.invocation.pushToLoggingContext()
log.debug { "Arguments: ${arguments.value.toTypedArray().contentDeepToString()}" }
rpcExecutor!!.submit {
val result = invokeRpc(context, clientToServer.methodName, arguments.value)
sendReply(clientToServer.replyId, clientToServer.clientAddress, result)
}
executeRpc(context, clientToServer, arguments, isQuickRpc)
}
is Try.Failure -> {
context = artemisMessage.context(clientToServer.sessionId, emptyList())
@ -483,7 +530,8 @@ class RPCServer(
val externalTrace = externalTrace()
val rpcActor = actorFrom(this)
val impersonatedActor = impersonatedActor()
return RpcAuthContext(InvocationContext.rpc(rpcActor.first, trace, externalTrace, impersonatedActor, arguments), rpcActor.second)
val serializedTelemetry = serializedTelemetry()
return RpcAuthContext(InvocationContext.rpc(rpcActor.first, trace, externalTrace, impersonatedActor, arguments, serializedTelemetry), rpcActor.second)
}
private fun actorFrom(message: ClientMessage): Pair<Actor, AuthorizingSubject> {

View File

@ -4,6 +4,7 @@ import net.corda.core.flows.Destination
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.internal.FlowIORequest
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
@ -72,7 +73,7 @@ sealed class Event {
* Initiate a flow. This causes a new session object to be created and returned to the flow. Note that no actual
* communication takes place at this time, only on the first send/receive operation on the session.
*/
data class InitiateFlow(val destination: Destination, val wellKnownParty: Party) : Event()
data class InitiateFlow(val destination: Destination, val wellKnownParty: Party, val serializedTelemetry: SerializedTelemetry?) : Event()
/**
* Signal the entering into a subflow.

View File

@ -142,7 +142,7 @@ class FlowCreator(
senderUUID: String?): Flow<A> {
// Before we construct the state machine state by freezing the FlowLogic we need to make sure that lazy properties
// have access to the fiber (and thereby the service hub)
val flowStateMachineImpl = FlowStateMachineImpl(flowId, flowLogic, scheduler)
val flowStateMachineImpl = FlowStateMachineImpl(flowId, flowLogic, scheduler, serializedTelemetry = (flowStart as? FlowStart.Initiated)?.initiatingMessage?.serializedTelemetry)
val resultFuture = openFuture<Any?>()
flowStateMachineImpl.transientValues = createTransientValues(flowId, resultFuture)
flowLogic.stateMachine = flowStateMachineImpl
@ -180,7 +180,7 @@ class FlowCreator(
return when(flowState) {
is FlowState.Unstarted -> {
val logic = deserializeFlowState(flowState.frozenFlowLogic)
FlowStateMachineImpl(runId, logic, scheduler)
FlowStateMachineImpl(runId, logic, scheduler, serializedTelemetry = null)
}
is FlowState.Started -> deserializeFlowState(flowState.frozenFiber)
// Places calling this function is rely on it to return null if the flow cannot be created from the checkpoint.

View File

@ -9,6 +9,8 @@ import net.corda.core.identity.Party
import net.corda.core.internal.FlowIORequest
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.checkPayloadIs
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.internal.telemetry.telemetryServiceInternal
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize
@ -18,7 +20,8 @@ import net.corda.core.utilities.UntrustworthyData
class FlowSessionImpl(
override val destination: Destination,
private val wellKnownParty: Party,
val sourceSessionId: SessionId
val sourceSessionId: SessionId,
val serializedTelemetry: SerializedTelemetry?
) : FlowSession() {
override val counterparty: Party get() = wellKnownParty
@ -30,6 +33,7 @@ class FlowSessionImpl(
override fun hashCode(): Int = sourceSessionId.hashCode()
private val flowStateMachine: FlowStateMachine<*> get() = Fiber.currentFiber() as FlowStateMachine<*>
private val telemetryMap = mapOf("destination" to destination.toString())
@Suspendable
override fun getCounterpartyFlowInfo(maySkipCheckpoint: Boolean): FlowInfo {
@ -46,6 +50,7 @@ class FlowSessionImpl(
payload: Any,
maySkipCheckpoint: Boolean
): UntrustworthyData<R> {
flowStateMachine.serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAndReceive", telemetryMap, flowLogic = flowStateMachine.logic) {
enforceNotPrimitive(receiveType)
val request = FlowIORequest.SendAndReceive(
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT)),
@ -53,30 +58,34 @@ class FlowSessionImpl(
)
val responseValues: Map<FlowSession, SerializedBytes<Any>> = flowStateMachine.suspend(request, maySkipCheckpoint)
val responseForCurrentSession = responseValues.getValue(this)
return responseForCurrentSession.checkPayloadIs(receiveType)
}
}
@Suspendable
override fun <R : Any> sendAndReceive(receiveType: Class<R>, payload: Any) = sendAndReceive(receiveType, payload, maySkipCheckpoint = false)
@Suspendable
override fun <R : Any> receive(receiveType: Class<R>, maySkipCheckpoint: Boolean): UntrustworthyData<R> {
flowStateMachine.serviceHub.telemetryServiceInternal.span("${this::class.java.name}#receive", telemetryMap, flowStateMachine.logic ) {
enforceNotPrimitive(receiveType)
val request = FlowIORequest.Receive(NonEmptySet.of(this))
return flowStateMachine.suspend(request, maySkipCheckpoint).getValue(this).checkPayloadIs(receiveType)
}
}
@Suspendable
override fun <R : Any> receive(receiveType: Class<R>) = receive(receiveType, maySkipCheckpoint = false)
@Suspendable
override fun send(payload: Any, maySkipCheckpoint: Boolean) {
flowStateMachine.serviceHub.telemetryServiceInternal.span("${this::class.java.name}#send", telemetryMap, flowStateMachine.logic) {
val request = FlowIORequest.Send(
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT))
)
return flowStateMachine.suspend(request, maySkipCheckpoint)
}
}
@Suspendable
override fun send(payload: Any) = send(payload, maySkipCheckpoint = false)

View File

@ -36,6 +36,9 @@ import net.corda.core.internal.isRegularFile
import net.corda.core.internal.location
import net.corda.core.internal.toPath
import net.corda.core.internal.uncheckedCast
import net.corda.core.internal.telemetry.ComponentTelemetryIds
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.internal.telemetry.telemetryServiceInternal
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.internal.CheckpointSerializationContext
@ -71,7 +74,8 @@ class TransientReference<out A>(@Transient val value: A)
class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
override val logic: FlowLogic<R>,
scheduler: FiberScheduler,
override val creationTime: Long = System.currentTimeMillis()
override val creationTime: Long = System.currentTimeMillis(),
val serializedTelemetry: SerializedTelemetry? = null
) : Fiber<Unit>(id.toString(), scheduler), FlowStateMachine<R>, FlowFiber {
companion object {
/**
@ -316,6 +320,9 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
}
private fun openThreadLocalWormhole() {
// This sets the Cordapp classloader on the contextClassLoader of the current thread.
// Needed because in previous versions of the finance app we used Thread.contextClassLoader to resolve services defined in cordapps.
Thread.currentThread().contextClassLoader = (serviceHub.cordappProvider as CordappProviderImpl).cordappLoader.appClassLoader
val threadLocal = transientValues.database.hikariPoolThreadLocal
if (threadLocal != null) {
val valueFromThread = swappedOutThreadLocalValue(threadLocal)
@ -346,8 +353,14 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
// Needed because in previous versions of the finance app we used Thread.contextClassLoader to resolve services defined in cordapps.
Thread.currentThread().contextClassLoader = (serviceHub.cordappProvider as CordappProviderImpl).cordappLoader.appClassLoader
val result = logic.call()
// context.serializedTelemetry is from an rpc client, serializedTelemetry is from a peer, otherwise nothing
val serializedTelemetrySrc = context.serializedTelemetry ?: serializedTelemetry
val result = serviceHub.telemetryServiceInternal.spanForFlow(logic.javaClass.name, emptyMap(), logic, serializedTelemetrySrc) {
val ret = logic.call()
// Note suspend stores the telemetry ids back in the components from checkpoint, so must be done, before we end the span
suspend(FlowIORequest.WaitForSessionConfirmations(), maySkipCheckpoint = true)
ret
}
Try.Success(result)
} catch (t: Throwable) {
if(t.isUnrecoverable()) {
@ -417,8 +430,11 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
isDbTransactionOpenOnExit = true
)
return try {
serviceHub.telemetryServiceInternal.span(subFlow.javaClass.name, emptyMap(), subFlow) {
subFlow.call()
} finally {
}
}
finally {
processEventImmediately(
Event.LeaveSubFlow,
isDbTransactionOpenOnEntry = true,
@ -457,10 +473,10 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
}
@Suspendable
override fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession {
override fun initiateFlow(destination: Destination, wellKnownParty: Party, serializedTelemetry: SerializedTelemetry?): FlowSession {
require(destination is Party || destination is AnonymousParty) { "Unsupported destination type ${destination.javaClass.name}" }
val resume = processEventImmediately(
Event.InitiateFlow(destination, wellKnownParty),
Event.InitiateFlow(destination, wellKnownParty, serializedTelemetry),
isDbTransactionOpenOnEntry = true,
isDbTransactionOpenOnExit = true
) as FlowContinuation.Resume
@ -527,6 +543,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
override fun <R : Any> suspend(ioRequest: FlowIORequest<R>, maySkipCheckpoint: Boolean): R {
val serializationContext = TransientReference(transientValues.checkpointSerializationContext)
val transaction = extractThreadLocalTransaction()
val telemetryIds = retrieveTelemetryIds()
parkAndSerialize { _, _ ->
setLoggingContext()
logger.trace { "Suspended on $ioRequest" }
@ -563,6 +580,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
}
}
storeTelemetryIds(telemetryIds)
transientState.reloadCheckpointAfterSuspendCount?.let { count ->
if (count < transientState.checkpoint.checkpointState.numberOfSuspends) {
onReloadFlowFromCheckpoint?.invoke(id)
@ -580,6 +598,16 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
))
}
private fun retrieveTelemetryIds(): ComponentTelemetryIds? {
return serviceHub.telemetryServiceInternal.getCurrentTelemetryIds()
}
private fun storeTelemetryIds(telemetryIds: ComponentTelemetryIds?) {
telemetryIds?.let {
serviceHub.telemetryServiceInternal.setCurrentTelemetryId(it)
}
}
private fun containsIdempotentFlows(): Boolean {
val subFlowStack = snapshot().checkpoint.checkpointState.subFlowStack
return subFlowStack.any { IdempotentFlow::class.java.isAssignableFrom(it.flowClass) }

View File

@ -2,6 +2,7 @@ package net.corda.node.services.statemachine
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowInfo
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import java.security.SecureRandom
@ -38,6 +39,7 @@ data class SessionId(val toLong: Long) {
* @param flowVersion the version of the initiating flow.
* @param appName the name of the cordapp defining the initiating flow, or "corda" if it's a core flow.
* @param firstPayload the optional first payload.
* @param serializedTelemetry the telemetry data
*/
data class InitialSessionMessage(
val initiatorSessionId: SessionId,
@ -45,7 +47,8 @@ data class InitialSessionMessage(
val initiatorFlowClassName: String,
val flowVersion: Int,
val appName: String,
val firstPayload: SerializedBytes<Any>?
val firstPayload: SerializedBytes<Any>?,
val serializedTelemetry: SerializedTelemetry?
) : SessionMessage() {
override fun toString() = "InitialSessionMessage(" +
"initiatorSessionId=$initiatorSessionId, " +
@ -53,6 +56,7 @@ data class InitialSessionMessage(
"initiatorFlowClassName=$initiatorFlowClassName, " +
"appName=$appName, " +
"firstPayload=${firstPayload?.javaClass}" +
"telemetryContext=$serializedTelemetry" +
")"
}

View File

@ -863,7 +863,7 @@ internal class SingleThreadedStateMachineManager(
try {
val initiatedFlowFactory = getInitiatedFlowFactory(sessionMessage)
val initiatedSessionId = SessionId.createRandom(secureRandom)
val senderSession = FlowSessionImpl(sender, sender, initiatedSessionId)
val senderSession = FlowSessionImpl(sender, sender, initiatedSessionId, sessionMessage.serializedTelemetry)
val flowLogic = initiatedFlowFactory.createFlow(senderSession)
val initiatedFlowInfo = when (initiatedFlowFactory) {
is InitiatedFlowFactory.Core -> FlowInfo(serviceHub.myInfo.platformVersion, "corda")

View File

@ -7,6 +7,7 @@ import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.Party
import net.corda.core.internal.DeclaredField
import net.corda.core.internal.FlowIORequest
import net.corda.core.internal.telemetry.SerializedTelemetry
import net.corda.core.serialization.SerializedBytes
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.toNonEmptySet
@ -74,7 +75,7 @@ class StartedFlowTransition(
return builder {
// Initialise uninitialised sessions in order to receive the associated FlowInfo. Some or all sessions may
// not be initialised yet.
sendInitialSessionMessagesIfNeeded(sessionIdToSession.keys)
sendInitialSessionMessagesIfNeeded(sessionIdToSession)
val flowInfoMap = getFlowInfoFromSessions(sessionIdToSession)
if (flowInfoMap == null) {
FlowContinuation.ProcessEvents
@ -140,7 +141,7 @@ class StartedFlowTransition(
sessionIdToSession[sessionId] = session
}
return builder {
sendToSessionsTransition(sessionIdToMessage)
sendToSessionsTransition(sessionIdToMessage, sessionIdToSession)
if (isErrored()) {
FlowContinuation.ProcessEvents
} else {
@ -202,7 +203,7 @@ class StartedFlowTransition(
sessionIdToSession[(session as FlowSessionImpl).sourceSessionId] = session
}
// send initialises to uninitialised sessions
sendInitialSessionMessagesIfNeeded(sessionIdToSession.keys)
sendInitialSessionMessagesIfNeeded(sessionIdToSession)
try {
val receivedMap = receiveFromSessionsTransition(sessionIdToSession)
if (receivedMap == null) {
@ -272,11 +273,11 @@ class StartedFlowTransition(
}
}
private fun TransitionBuilder.sendInitialSessionMessagesIfNeeded(sourceSessions: Set<SessionId>) {
private fun TransitionBuilder.sendInitialSessionMessagesIfNeeded(sessionIdToSession: Map<SessionId, FlowSessionImpl>) {
val checkpoint = startingState.checkpoint
val newSessions = LinkedHashMap<SessionId, SessionState>(checkpoint.checkpointState.sessions)
var index = 0
for (sourceSessionId in sourceSessions) {
for (sourceSessionId in sessionIdToSession.keys) {
val sessionState = checkpoint.checkpointState.sessions[sourceSessionId]
if (sessionState == null) {
return freshErrorTransition(CannotFindSessionException(sourceSessionId))
@ -284,7 +285,8 @@ class StartedFlowTransition(
if (sessionState !is SessionState.Uninitiated) {
continue
}
val initialMessage = createInitialSessionMessage(sessionState.initiatingSubFlow, sourceSessionId, sessionState.additionalEntropy, null)
val telemetryData = sessionIdToSession[sourceSessionId]?.serializedTelemetry
val initialMessage = createInitialSessionMessage(sessionState.initiatingSubFlow, sourceSessionId, sessionState.additionalEntropy, null, telemetryData)
val newSessionState = SessionState.Initiating(
bufferedMessages = arrayListOf(),
rejectionError = null,
@ -302,7 +304,8 @@ class StartedFlowTransition(
val sessionIdToMessage = flowIORequest.sessionToMessage.mapKeys {
sessionToSessionId(it.key)
}
sendToSessionsTransition(sessionIdToMessage)
val sessionIdToSession = flowIORequest.sessionToMessage.map { sessionToSessionId(it.key) to it.key as FlowSessionImpl}.toMap()
sendToSessionsTransition(sessionIdToMessage, sessionIdToSession)
if (isErrored()) {
FlowContinuation.ProcessEvents
} else {
@ -311,7 +314,8 @@ class StartedFlowTransition(
}
}
private fun TransitionBuilder.sendToSessionsTransition(sourceSessionIdToMessage: Map<SessionId, SerializedBytes<Any>>) {
private fun TransitionBuilder.sendToSessionsTransition(sourceSessionIdToMessage: Map<SessionId, SerializedBytes<Any>>, sourceSessionIdToSession: Map<SessionId, FlowSessionImpl
>) {
val checkpoint = startingState.checkpoint
val newSessions = LinkedHashMap(checkpoint.checkpointState.sessions)
var index = 0
@ -323,7 +327,8 @@ class StartedFlowTransition(
val sendInitialActions = messagesByType[SessionState.Uninitiated::class]?.map { (sourceSessionId, sessionState, message) ->
val uninitiatedSessionState = sessionState as SessionState.Uninitiated
val deduplicationId = DeduplicationId.createForNormal(checkpoint, index++, sessionState)
val initialMessage = createInitialSessionMessage(uninitiatedSessionState.initiatingSubFlow, sourceSessionId, uninitiatedSessionState.additionalEntropy, message)
val serializedTelemetry: SerializedTelemetry? = sourceSessionIdToSession[sourceSessionId]?.serializedTelemetry
val initialMessage = createInitialSessionMessage(uninitiatedSessionState.initiatingSubFlow, sourceSessionId, uninitiatedSessionState.additionalEntropy, message, serializedTelemetry)
newSessions[sourceSessionId] = SessionState.Initiating(
bufferedMessages = arrayListOf(),
rejectionError = null,
@ -495,7 +500,8 @@ class StartedFlowTransition(
initiatingSubFlow: SubFlow.Initiating,
sourceSessionId: SessionId,
additionalEntropy: Long,
payload: SerializedBytes<Any>?
payload: SerializedBytes<Any>?,
serializedTelemetry: SerializedTelemetry?
): InitialSessionMessage {
return InitialSessionMessage(
initiatorSessionId = sourceSessionId,
@ -504,7 +510,8 @@ class StartedFlowTransition(
initiatorFlowClassName = initiatingSubFlow.classToInitiateWith.name,
flowVersion = initiatingSubFlow.flowInfo.flowVersion,
appName = initiatingSubFlow.flowInfo.appName,
firstPayload = payload
firstPayload = payload,
serializedTelemetry = serializedTelemetry
)
}

View File

@ -305,7 +305,7 @@ class TopLevelTransition(
return@builder FlowContinuation.ProcessEvents
}
val sourceSessionId = SessionId.createRandom(context.secureRandom)
val sessionImpl = FlowSessionImpl(event.destination, event.wellKnownParty, sourceSessionId)
val sessionImpl = FlowSessionImpl(event.destination, event.wellKnownParty, sourceSessionId, event.serializedTelemetry)
val newSessions = checkpoint.checkpointState.sessions + (sourceSessionId to SessionState.Uninitiated(event.destination, initiatingSubFlow, sourceSessionId, context.secureRandom.nextLong()))
currentState = currentState.copy(checkpoint = checkpoint.setSessions(newSessions))
actions.add(Action.AddSessionBinding(context.id, sourceSessionId))

View File

@ -25,3 +25,9 @@ rpcSettings = {
trustStorePassword = "trustpass"
useTestClock = false
verifierType = InMemory
telemetry {
openTelemetryEnabled = true,
simpleLogTelemetryEnabled = false,
spanStartEndEventsEnabled = false,
copyBaggageToTags = false
}

View File

@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.atLeast
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.VersionInfo
@ -134,6 +135,8 @@ class NodeH2SecurityTests {
whenever(config.dataSourceProperties).thenReturn(hikaryProperties)
whenever(config.baseDirectory).thenReturn(mock())
whenever(config.effectiveH2Settings).thenAnswer { NodeH2Settings(address) }
whenever(config.telemetry).thenReturn(mock())
whenever(config.myLegalName).thenReturn(CordaX500Name(null, "client-${address.toString()}", "Corda", "London", null, "GB"))
}
private inner class MockNode: Node(config, VersionInfo.UNKNOWN, false) {

Some files were not shown because too many files have changed in this diff Show More