mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge branch 'release/os/4.10' into merge-release/os/4.9-release/os/4.10-2024-11-25-422
This commit is contained in:
commit
2ed9753f87
@ -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
|
||||
|
4
.ci/dev/forward-merge/Jenkinsfile
vendored
4
.ci/dev/forward-merge/Jenkinsfile
vendored
@ -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
|
||||
|
1
.github/workflows/jira_close_issue.yml
vendored
1
.github/workflows/jira_close_issue.yml
vendored
@ -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 }}
|
||||
|
5
.idea/codeStyles/Project.xml
generated
5
.idea/codeStyles/Project.xml
generated
@ -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" />
|
||||
|
1
.idea/codeStyles/codeStyleConfig.xml
generated
1
.idea/codeStyles/codeStyleConfig.xml
generated
@ -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
279
.snyk
Normal file → Executable file
@ -8,7 +8,7 @@ ignore:
|
||||
Guava’s Files.createTempDir() is used during integration tests only.
|
||||
Users of Corda are advised not to use Guava’s 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 okhttp’s 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: {}
|
||||
|
@ -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/)
|
||||
|
||||
|
174
build.gradle
174
build.gradle
@ -56,21 +56,20 @@ 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.13.5'
|
||||
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 +78,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 = '4.0.3'
|
||||
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 +216,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'
|
||||
}
|
||||
}
|
||||
@ -492,7 +487,7 @@ allprojects {
|
||||
// * Apache commons-configuration2
|
||||
eachDependency { details ->
|
||||
if (details.requested.group == 'io.netty' && details.requested.name.startsWith('netty-')) {
|
||||
if (details.requested.name.startsWith('netty-tcnative')){
|
||||
if (details.requested.name.startsWith('netty-tcnative')) {
|
||||
details.useVersion tcnative_version
|
||||
} else {
|
||||
details.useVersion netty_version
|
||||
@ -510,25 +505,48 @@ allprojects {
|
||||
details.useVersion snake_yaml_version
|
||||
}
|
||||
}
|
||||
|
||||
dependencySubstitution {
|
||||
// We want to use SLF4J's version of these bindings: jcl-over-slf4j
|
||||
// Remove any transitive dependency on Apache's version.
|
||||
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)
|
||||
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.
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
compile {
|
||||
// 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'
|
||||
// Remove any transitive dependency on Logback (e.g. Liquibase 3.6 introduces this dependency)
|
||||
exclude group: 'ch.qos.logback'
|
||||
|
||||
// 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'
|
||||
// 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 +684,8 @@ bintrayConfig {
|
||||
gpgSign = true
|
||||
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
|
||||
publications = [
|
||||
'corda-opentelemetry',
|
||||
'corda-opentelemetry-driver',
|
||||
'corda-jfx',
|
||||
'corda-mock',
|
||||
'corda-rpc',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
@ -203,11 +203,14 @@
|
||||
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
|
||||
</Logger>
|
||||
<Logger name="org.apache.activemq.artemis.core.server" level="warn" additivity="false">
|
||||
<Filters>
|
||||
<RegexFilter regex=".*AMQ222165.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<RegexFilter regex=".*AMQ222166.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
</Filters>
|
||||
<AppenderRef ref="Console-ErrorCode-Selector"/>
|
||||
<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>
|
||||
<AppenderRef ref="RollingFile-ErrorCode-Appender"/>
|
||||
</Logger>
|
||||
<Logger name="org.apache.activemq.audit" level="error" additivity="false">
|
||||
|
@ -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=4.0.3
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
@ -120,8 +123,17 @@ data class SignatureAttachmentConstraint(val key: PublicKey) : AttachmentConstra
|
||||
return if (!key.isFulfilledBy(attachment.signerKeys.map { it })) {
|
||||
if (!disableWarnings) 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<*, *>>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() }))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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,18 +222,22 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
||||
|
||||
@Suspendable
|
||||
private fun notariseAndRecord(): SignedTransaction {
|
||||
val notarised = if (needsNotarySignature(transaction)) {
|
||||
progressTracker.currentStep = NOTARISING
|
||||
val notarySignatures = subFlow(NotaryFlow.Client(transaction, skipVerification = true))
|
||||
transaction + notarySignatures
|
||||
} else {
|
||||
logger.info("No need to notarise this transaction.")
|
||||
transaction
|
||||
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))
|
||||
transaction + notarySignatures
|
||||
} else {
|
||||
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
|
||||
}
|
||||
logger.info("Recording transaction locally.")
|
||||
serviceHub.recordTransactions(statesToRecord, listOf(notarised))
|
||||
logger.info("Recorded transaction locally successfully.")
|
||||
return notarised
|
||||
}
|
||||
|
||||
private fun needsNotarySignature(stx: SignedTransaction): Boolean {
|
||||
|
@ -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,11 +286,13 @@ abstract class FlowLogic<out T> {
|
||||
|
||||
@Suspendable
|
||||
internal fun <R : Any> FlowSession.sendAndReceiveWithRetry(receiveType: Class<R>, payload: Any): UntrustworthyData<R> {
|
||||
val request = FlowIORequest.SendAndReceive(
|
||||
sessionToMessage = stateMachine.serialize(mapOf(this to payload)),
|
||||
shouldRetrySend = true
|
||||
)
|
||||
return stateMachine.suspend(request, maySkipCheckpoint = false)[this]!!.checkPayloadIs(receiveType)
|
||||
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
|
||||
@ -310,12 +313,14 @@ abstract class FlowLogic<out T> {
|
||||
@Suspendable
|
||||
@JvmOverloads
|
||||
open fun receiveAllMap(sessions: Map<FlowSession, Class<out Any>>, maySkipCheckpoint: Boolean = false): Map<FlowSession, UntrustworthyData<Any>> {
|
||||
enforceNoPrimitiveInReceive(sessions.values)
|
||||
val replies = stateMachine.suspend(
|
||||
ioRequest = FlowIORequest.Receive(sessions.keys.toNonEmptySet()),
|
||||
maySkipCheckpoint = maySkipCheckpoint
|
||||
)
|
||||
return replies.mapValues { (session, payload) -> payload.checkPayloadIs(sessions[session]!!) }
|
||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#receiveAllMap") {
|
||||
enforceNoPrimitiveInReceive(sessions.values)
|
||||
val replies = stateMachine.suspend(
|
||||
ioRequest = FlowIORequest.Receive(sessions.keys.toNonEmptySet()),
|
||||
maySkipCheckpoint = maySkipCheckpoint
|
||||
)
|
||||
return replies.mapValues { (session, payload) -> payload.checkPayloadIs(sessions[session]!!) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,9 +337,11 @@ abstract class FlowLogic<out T> {
|
||||
@Suspendable
|
||||
@JvmOverloads
|
||||
open fun <R : Any> receiveAll(receiveType: Class<R>, sessions: List<FlowSession>, maySkipCheckpoint: Boolean = false): List<UntrustworthyData<R>> {
|
||||
enforceNoPrimitiveInReceive(listOf(receiveType))
|
||||
enforceNoDuplicates(sessions)
|
||||
return castMapValuesToKnownType(receiveAllMap(associateSessionsToReceiveType(receiveType, sessions)))
|
||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#receiveAll") {
|
||||
enforceNoPrimitiveInReceive(listOf(receiveType))
|
||||
enforceNoDuplicates(sessions)
|
||||
return castMapValuesToKnownType(receiveAllMap(associateSessionsToReceiveType(receiveType, sessions)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -351,8 +358,10 @@ abstract class FlowLogic<out T> {
|
||||
@Suspendable
|
||||
@JvmOverloads
|
||||
fun sendAll(payload: Any, sessions: Set<FlowSession>, maySkipCheckpoint: Boolean = false) {
|
||||
val sessionToPayload = sessions.map { it to payload }.toMap()
|
||||
return sendAllMap(sessionToPayload, maySkipCheckpoint)
|
||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAll") {
|
||||
val sessionToPayload = sessions.map { it to payload }.toMap()
|
||||
return sendAllMap(sessionToPayload, maySkipCheckpoint)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,10 +377,12 @@ abstract class FlowLogic<out T> {
|
||||
@Suspendable
|
||||
@JvmOverloads
|
||||
fun sendAllMap(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) {
|
||||
val request = FlowIORequest.Send(
|
||||
sessionToMessage = stateMachine.serialize(payloadsPerSession)
|
||||
)
|
||||
stateMachine.suspend(request, maySkipCheckpoint)
|
||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#sendAllMap") {
|
||||
val request = FlowIORequest.Send(
|
||||
sessionToMessage = stateMachine.serialize(payloadsPerSession)
|
||||
)
|
||||
stateMachine.suspend(request, maySkipCheckpoint)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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>)
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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,14 +75,15 @@ abstract class NotaryServiceFlow(
|
||||
sleep(Duration.ZERO)
|
||||
}
|
||||
}
|
||||
|
||||
service.commitInputStates(
|
||||
tx.inputs,
|
||||
tx.id,
|
||||
otherSideSession.counterparty,
|
||||
requestPayload.requestSignature,
|
||||
tx.timeWindow,
|
||||
tx.references)
|
||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#call:commitInputStates", flowLogic = this) {
|
||||
service.commitInputStates(
|
||||
tx.inputs,
|
||||
tx.id,
|
||||
otherSideSession.counterparty,
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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}}")
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
||||
}
|
@ -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)))
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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?
|
||||
}
|
@ -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!!)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ->
|
||||
|
@ -692,7 +692,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,
|
||||
|
@ -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.
|
||||
|
@ -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/*
|
||||
|
@ -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(
|
||||
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()
|
||||
)
|
||||
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(),
|
||||
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 {
|
||||
|
@ -0,0 +1,8 @@
|
||||
package net.corda.nodeapi.internal
|
||||
|
||||
enum class NodeStatus {
|
||||
WAITING_TO_START,
|
||||
STARTING,
|
||||
STARTED,
|
||||
STOPPING
|
||||
}
|
@ -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)
|
||||
|
@ -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) }
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
if (targetIndex == -1) {
|
||||
log.error("No targets have presented acceptable certificates for $allowedRemoteLegalNames. Halting retries")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
}
|
||||
log.info("Retry connect to ${targets[targetIndex]}")
|
||||
retryInterval = 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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
|
||||
enum class ConnectionResult {
|
||||
NO_ERROR,
|
||||
HANDSHAKE_FAILURE
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
@ -178,7 +199,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `serialize - deserialize SignableData`() {
|
||||
fun `serialize - deserialize SignableData`() {
|
||||
val testString = "Hello World"
|
||||
val testBytes = testString.toByteArray()
|
||||
|
||||
@ -186,15 +207,33 @@ 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 Logger`() {
|
||||
@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)
|
||||
fun `serialize - deserialize Logger`() {
|
||||
val storageContext: CheckpointSerializationContext = context
|
||||
val logger = LoggerFactory.getLogger("aName")
|
||||
val logger2 = logger.checkpointSerialize(storageContext).checkpointDeserialize(storageContext)
|
||||
assertEquals(logger.name, logger2.name)
|
||||
assertTrue(logger === logger2)
|
||||
assertSame(logger, logger2)
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
|
@ -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 {
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,19 +855,25 @@ class FlowEntityManagerTest : AbstractFlowEntityManagerTest() {
|
||||
init {
|
||||
if (includeRawUpdates) {
|
||||
services.register {
|
||||
services.vaultService.rawUpdates.subscribe {
|
||||
if (insertionType == InsertionType.ENTITY_MANAGER) {
|
||||
services.withEntityManager {
|
||||
persist(entityWithIdOne)
|
||||
persist(entityWithIdTwo)
|
||||
persist(entityWithIdThree)
|
||||
}
|
||||
} else {
|
||||
services.jdbcSession().run {
|
||||
insert(entityWithIdOne)
|
||||
insert(entityWithIdTwo)
|
||||
insert(entityWithIdThree)
|
||||
}
|
||||
processEvent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processEvent(event : ServiceLifecycleEvent) {
|
||||
if (event == ServiceLifecycleEvent.STATE_MACHINE_STARTED) {
|
||||
services.vaultService.rawUpdates.subscribe {
|
||||
if (insertionType == InsertionType.ENTITY_MANAGER) {
|
||||
services.withEntityManager {
|
||||
persist(entityWithIdOne)
|
||||
persist(entityWithIdTwo)
|
||||
persist(entityWithIdThree)
|
||||
}
|
||||
} else {
|
||||
services.jdbcSession().run {
|
||||
insert(entityWithIdOne)
|
||||
insert(entityWithIdTwo)
|
||||
insert(entityWithIdThree)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,12 +51,14 @@ class CordaServiceLifecycleFatalTests {
|
||||
|
||||
object FailingObserver : ServiceLifecycleObserver {
|
||||
override fun onServiceLifecycleEvent(event: ServiceLifecycleEvent) {
|
||||
val tmpFile = File(System.getProperty(tempFilePropertyName))
|
||||
tmpFile.appendText("\n" + readyToThrowMarker)
|
||||
eventually(duration = 30.seconds) {
|
||||
assertEquals(goodToThrowMarker, tmpFile.readLines().last())
|
||||
if (event == ServiceLifecycleEvent.STATE_MACHINE_STARTED) {
|
||||
val tmpFile = File(System.getProperty(tempFilePropertyName))
|
||||
tmpFile.appendText("\n" + readyToThrowMarker)
|
||||
eventually(duration = 30.seconds) {
|
||||
assertEquals(goodToThrowMarker, tmpFile.readLines().last())
|
||||
}
|
||||
throw CordaServiceCriticalFailureException("controlled failure")
|
||||
}
|
||||
throw CordaServiceCriticalFailureException("controlled failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,17 +104,14 @@ class CordaServiceLifecycleTests {
|
||||
class TextLengthComputingService(services: AppServiceHub) : SingletonSerializeAsToken() {
|
||||
|
||||
init {
|
||||
services.register { addEvent(it) }
|
||||
for (n in 1..numServiceCallbacks) {
|
||||
services.register { addEvent(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addEvent(event: ServiceLifecycleEvent) {
|
||||
when (event) {
|
||||
STATE_MACHINE_STARTED -> {
|
||||
eventsCaptured.add(event)
|
||||
}
|
||||
else -> {
|
||||
eventsCaptured.add(event)
|
||||
}
|
||||
if (event in eventsToBeCaptured) {
|
||||
eventsCaptured.add(event)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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],
|
||||
|
@ -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,12 +136,14 @@ class BasicHSMKeyManagementService(
|
||||
}
|
||||
|
||||
override fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey {
|
||||
val signingPublicKey = getSigningPublicKey(publicKey)
|
||||
return if (signingPublicKey in originalKeysMap) {
|
||||
DigitalSignature.WithKey(signingPublicKey, cryptoService.sign(originalKeysMap[signingPublicKey]!!, bytes))
|
||||
} else {
|
||||
val keyPair = getSigningKeyPair(signingPublicKey)
|
||||
keyPair.sign(bytes)
|
||||
telemetryService.span("${this::class.java.name}#sign") {
|
||||
val signingPublicKey = getSigningPublicKey(publicKey)
|
||||
return if (signingPublicKey in originalKeysMap) {
|
||||
DigitalSignature.WithKey(signingPublicKey, cryptoService.sign(originalKeysMap[signingPublicKey]!!, bytes))
|
||||
} else {
|
||||
val keyPair = getSigningKeyPair(signingPublicKey)
|
||||
keyPair.sign(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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,15 +50,16 @@ class FlowSessionImpl(
|
||||
payload: Any,
|
||||
maySkipCheckpoint: Boolean
|
||||
): UntrustworthyData<R> {
|
||||
enforceNotPrimitive(receiveType)
|
||||
val request = FlowIORequest.SendAndReceive(
|
||||
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT)),
|
||||
shouldRetrySend = false
|
||||
)
|
||||
val responseValues: Map<FlowSession, SerializedBytes<Any>> = flowStateMachine.suspend(request, maySkipCheckpoint)
|
||||
val responseForCurrentSession = responseValues.getValue(this)
|
||||
|
||||
return responseForCurrentSession.checkPayloadIs(receiveType)
|
||||
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)),
|
||||
shouldRetrySend = false
|
||||
)
|
||||
val responseValues: Map<FlowSession, SerializedBytes<Any>> = flowStateMachine.suspend(request, maySkipCheckpoint)
|
||||
val responseForCurrentSession = responseValues.getValue(this)
|
||||
return responseForCurrentSession.checkPayloadIs(receiveType)
|
||||
}
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
@ -62,9 +67,11 @@ class FlowSessionImpl(
|
||||
|
||||
@Suspendable
|
||||
override fun <R : Any> receive(receiveType: Class<R>, maySkipCheckpoint: Boolean): UntrustworthyData<R> {
|
||||
enforceNotPrimitive(receiveType)
|
||||
val request = FlowIORequest.Receive(NonEmptySet.of(this))
|
||||
return flowStateMachine.suspend(request, maySkipCheckpoint).getValue(this).checkPayloadIs(receiveType)
|
||||
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
|
||||
@ -72,10 +79,12 @@ class FlowSessionImpl(
|
||||
|
||||
@Suspendable
|
||||
override fun send(payload: Any, maySkipCheckpoint: Boolean) {
|
||||
val request = FlowIORequest.Send(
|
||||
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT))
|
||||
)
|
||||
return flowStateMachine.suspend(request, maySkipCheckpoint)
|
||||
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
|
||||
|
@ -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()
|
||||
suspend(FlowIORequest.WaitForSessionConfirmations(), maySkipCheckpoint = true)
|
||||
// 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 {
|
||||
subFlow.call()
|
||||
} finally {
|
||||
serviceHub.telemetryServiceInternal.span(subFlow.javaClass.name, emptyMap(), subFlow) {
|
||||
subFlow.call()
|
||||
}
|
||||
}
|
||||
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) }
|
||||
|
@ -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" +
|
||||
")"
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -25,3 +25,9 @@ rpcSettings = {
|
||||
trustStorePassword = "trustpass"
|
||||
useTestClock = false
|
||||
verifierType = InMemory
|
||||
telemetry {
|
||||
openTelemetryEnabled = true,
|
||||
simpleLogTelemetryEnabled = false,
|
||||
spanStartEndEventsEnabled = false,
|
||||
copyBaggageToTags = false
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user