mirror of
https://github.com/corda/corda.git
synced 2025-01-26 14:19:23 +00:00
[CORDA-760]: Propagate invocation context across the codebase. (#2016)
This commit is contained in:
parent
f0a5ea96e7
commit
92c8861802
@ -53,6 +53,154 @@ public interface net.corda.core.concurrent.CordaFuture extends java.util.concurr
|
||||
public abstract void then(kotlin.jvm.functions.Function1)
|
||||
@org.jetbrains.annotations.NotNull public abstract concurrent.CompletableFuture toCompletableFuture()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.Actor extends java.lang.Object
|
||||
public <init>(net.corda.core.context.Actor$Id, net.corda.core.context.AuthServiceId, net.corda.core.identity.CordaX500Name)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Actor$Id component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.AuthServiceId component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component3()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Actor copy(net.corda.core.context.Actor$Id, net.corda.core.context.AuthServiceId, net.corda.core.identity.CordaX500Name)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Actor$Id getId()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getOwningLegalIdentity()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.AuthServiceId getServiceId()
|
||||
public int hashCode()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.Actor service(String, net.corda.core.identity.CordaX500Name)
|
||||
public String toString()
|
||||
public static final net.corda.core.context.Actor$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.context.Actor$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Actor service(String, net.corda.core.identity.CordaX500Name)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Actor$Id extends java.lang.Object
|
||||
public <init>(String)
|
||||
@org.jetbrains.annotations.NotNull public final String component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Actor$Id copy(String)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final String getValue()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.AuthServiceId extends java.lang.Object
|
||||
public <init>(String)
|
||||
@org.jetbrains.annotations.NotNull public final String component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.AuthServiceId copy(String)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final String getValue()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.InvocationContext extends java.lang.Object
|
||||
public <init>(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace component2()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor component3()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Trace component4()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor component5()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext copy(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor getActor()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Trace getExternalTrace()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.Actor getImpersonatedActor()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin getOrigin()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace getTrace()
|
||||
public int hashCode()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@org.jetbrains.annotations.NotNull public final java.security.Principal principal()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.InvocationContext shell(net.corda.core.context.Trace, net.corda.core.context.Trace)
|
||||
public String toString()
|
||||
public static final net.corda.core.context.InvocationContext$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.context.InvocationContext$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext newInstance(net.corda.core.context.Origin, net.corda.core.context.Trace, net.corda.core.context.Actor, net.corda.core.context.Trace, net.corda.core.context.Actor)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext scheduled(net.corda.core.contracts.ScheduledStateRef, net.corda.core.context.Trace, net.corda.core.context.Trace)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.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)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext shell(net.corda.core.context.Trace, net.corda.core.context.Trace)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.context.Origin extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public abstract java.security.Principal principal()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Peer extends net.corda.core.context.Origin
|
||||
public <init>(net.corda.core.identity.CordaX500Name)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Peer copy(net.corda.core.identity.CordaX500Name)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getParty()
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public java.security.Principal principal()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$RPC extends net.corda.core.context.Origin
|
||||
public <init>(net.corda.core.context.Actor)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$RPC copy(net.corda.core.context.Actor)
|
||||
public boolean equals(Object)
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public java.security.Principal principal()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Scheduled extends net.corda.core.context.Origin
|
||||
public <init>(net.corda.core.contracts.ScheduledStateRef)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.ScheduledStateRef component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Scheduled copy(net.corda.core.contracts.ScheduledStateRef)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.ScheduledStateRef getScheduledState()
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public java.security.Principal principal()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Service extends net.corda.core.context.Origin
|
||||
public <init>(String, net.corda.core.identity.CordaX500Name)
|
||||
@org.jetbrains.annotations.NotNull public final String component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Origin$Service copy(String, net.corda.core.identity.CordaX500Name)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.CordaX500Name getOwningLegalIdentity()
|
||||
@org.jetbrains.annotations.NotNull public final String getServiceClassName()
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public java.security.Principal principal()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Origin$Shell extends net.corda.core.context.Origin
|
||||
@org.jetbrains.annotations.NotNull public java.security.Principal principal()
|
||||
public static final net.corda.core.context.Origin$Shell INSTANCE
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.context.Trace extends java.lang.Object
|
||||
public <init>(net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$InvocationId component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$SessionId component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace copy(net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$InvocationId getInvocationId()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$SessionId getSessionId()
|
||||
public int hashCode()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.Trace newInstance(net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId)
|
||||
public String toString()
|
||||
public static final net.corda.core.context.Trace$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.context.Trace$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace newInstance(net.corda.core.context.Trace$InvocationId, net.corda.core.context.Trace$SessionId)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Trace$InvocationId extends net.corda.core.utilities.Id
|
||||
public <init>(String, java.time.Instant)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.Trace$InvocationId newInstance(String, java.time.Instant)
|
||||
public static final net.corda.core.context.Trace$InvocationId$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.context.Trace$InvocationId$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$InvocationId newInstance(String, java.time.Instant)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.context.Trace$SessionId extends net.corda.core.utilities.Id
|
||||
public <init>(String, java.time.Instant)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.context.Trace$SessionId newInstance(String, java.time.Instant)
|
||||
public static final net.corda.core.context.Trace$SessionId$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.context.Trace$SessionId$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$SessionId newInstance(String, java.time.Instant)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.AlwaysAcceptAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
|
||||
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
|
||||
public static final net.corda.core.contracts.AlwaysAcceptAttachmentConstraint INSTANCE
|
||||
@ -283,7 +431,7 @@ public final class net.corda.core.contracts.ScheduledActivity extends java.lang.
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
public final class net.corda.core.contracts.ScheduledStateRef extends java.lang.Object implements net.corda.core.contracts.Scheduled
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.ScheduledStateRef extends java.lang.Object implements net.corda.core.contracts.Scheduled
|
||||
public <init>(net.corda.core.contracts.StateRef, java.time.Instant)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.StateRef component1()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Instant component2()
|
||||
@ -351,6 +499,7 @@ public final class net.corda.core.contracts.Structures extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.contracts.TimeWindow fromOnly(java.time.Instant)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.contracts.TimeWindow fromStartAndDuration(java.time.Instant, java.time.Duration)
|
||||
@org.jetbrains.annotations.Nullable public abstract java.time.Instant getFromTime()
|
||||
@org.jetbrains.annotations.Nullable public final java.time.Duration getLength()
|
||||
@org.jetbrains.annotations.Nullable public abstract java.time.Instant getMidpoint()
|
||||
@org.jetbrains.annotations.Nullable public abstract java.time.Instant getUntilTime()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.contracts.TimeWindow untilOnly(java.time.Instant)
|
||||
@ -418,6 +567,7 @@ public final class net.corda.core.contracts.TransactionStateKt extends java.lang
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$MissingAttachmentRejection extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String)
|
||||
@org.jetbrains.annotations.NotNull public final String getContractClass()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$MoreThanOneNotary extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash)
|
||||
@ -675,6 +825,7 @@ public final class net.corda.core.crypto.CryptoUtils extends java.lang.Object
|
||||
public final boolean isValid(byte[])
|
||||
public final boolean verify(net.corda.core.utilities.OpaqueBytes)
|
||||
public final boolean verify(byte[])
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.DigitalSignature withoutKey()
|
||||
##
|
||||
public abstract class net.corda.core.crypto.MerkleTree extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash getHash()
|
||||
@ -1038,6 +1189,16 @@ public static final class net.corda.core.flows.FinalityFlow$Companion extends ja
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.FlowInitiator$Service extends net.corda.core.flows.FlowInitiator
|
||||
public <init>(String)
|
||||
@org.jetbrains.annotations.NotNull public final String component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator$Service copy(String)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public String getName()
|
||||
@org.jetbrains.annotations.NotNull public final String getServiceClassName()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.FlowInitiator$Shell extends net.corda.core.flows.FlowInitiator
|
||||
@org.jetbrains.annotations.NotNull public String getName()
|
||||
public static final net.corda.core.flows.FlowInitiator$Shell INSTANCE
|
||||
@ -1047,6 +1208,7 @@ public abstract class net.corda.core.flows.FlowLogic extends java.lang.Object
|
||||
@co.paralleluniverse.fibers.Suspendable public abstract Object call()
|
||||
public final void checkFlowPermission(String, Map)
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.Nullable public final net.corda.core.flows.FlowStackSnapshot flowStackSnapshot()
|
||||
@org.jetbrains.annotations.Nullable public static final net.corda.core.flows.FlowLogic getCurrentTopLevel()
|
||||
@kotlin.Deprecated @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInfo getFlowInfo(net.corda.core.identity.Party)
|
||||
@org.jetbrains.annotations.NotNull public final org.slf4j.Logger getLogger()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getOurIdentity()
|
||||
@ -1058,13 +1220,22 @@ public abstract class net.corda.core.flows.FlowLogic extends java.lang.Object
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowSession initiateFlow(net.corda.core.identity.Party)
|
||||
@co.paralleluniverse.fibers.Suspendable public final void persistFlowStackSnapshot()
|
||||
@kotlin.Deprecated @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public net.corda.core.utilities.UntrustworthyData receive(Class, net.corda.core.identity.Party)
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public List receiveAll(Class, List)
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public Map receiveAll(Map)
|
||||
public final void recordAuditEvent(String, String, Map)
|
||||
@kotlin.Deprecated @co.paralleluniverse.fibers.Suspendable public void send(net.corda.core.identity.Party, Object)
|
||||
@kotlin.Deprecated @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public net.corda.core.utilities.UntrustworthyData sendAndReceive(Class, net.corda.core.identity.Party, Object)
|
||||
public final void setStateMachine(net.corda.core.internal.FlowStateMachine)
|
||||
@co.paralleluniverse.fibers.Suspendable @kotlin.jvm.JvmStatic public static final void sleep(java.time.Duration)
|
||||
@co.paralleluniverse.fibers.Suspendable public Object subFlow(net.corda.core.flows.FlowLogic)
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.messaging.DataFeed track()
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction waitForLedgerCommit(net.corda.core.crypto.SecureHash)
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction waitForLedgerCommit(net.corda.core.crypto.SecureHash, boolean)
|
||||
public static final net.corda.core.flows.FlowLogic$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.flows.FlowLogic$Companion extends java.lang.Object
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.flows.FlowLogic getCurrentTopLevel()
|
||||
@co.paralleluniverse.fibers.Suspendable @kotlin.jvm.JvmStatic public final void sleep(java.time.Duration)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public interface net.corda.core.flows.FlowLogicRef
|
||||
##
|
||||
@ -1185,6 +1356,7 @@ public final class net.corda.core.flows.ReceiveStateAndRefFlow extends net.corda
|
||||
public final class net.corda.core.flows.ReceiveTransactionFlow extends net.corda.core.flows.FlowLogic
|
||||
public <init>(net.corda.core.flows.FlowSession)
|
||||
public <init>(net.corda.core.flows.FlowSession, boolean)
|
||||
public <init>(net.corda.core.flows.FlowSession, boolean, net.corda.core.node.StatesToRecord)
|
||||
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public net.corda.core.transactions.SignedTransaction call()
|
||||
##
|
||||
public @interface net.corda.core.flows.SchedulableFlow
|
||||
@ -1227,6 +1399,8 @@ public final class net.corda.core.flows.StackFrameDataToken extends java.lang.Ob
|
||||
##
|
||||
public @interface net.corda.core.flows.StartableByRPC
|
||||
##
|
||||
public @interface net.corda.core.flows.StartableByService
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.StateMachineRunId extends java.lang.Object
|
||||
public <init>(UUID)
|
||||
@org.jetbrains.annotations.NotNull public final UUID component1()
|
||||
@ -1359,6 +1533,7 @@ public final class net.corda.core.identity.IdentityUtils extends java.lang.Objec
|
||||
@org.jetbrains.annotations.NotNull public abstract List networkMapSnapshot()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NodeInfo nodeInfo()
|
||||
@org.jetbrains.annotations.Nullable public abstract net.corda.core.node.NodeInfo nodeInfoFromParty(net.corda.core.identity.AbstractParty)
|
||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract rx.Observable nodeStateObservable()
|
||||
@org.jetbrains.annotations.NotNull public abstract List notaryIdentities()
|
||||
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party notaryPartyFromX500Name(net.corda.core.identity.CordaX500Name)
|
||||
@org.jetbrains.annotations.NotNull public abstract java.io.InputStream openAttachment(net.corda.core.crypto.SecureHash)
|
||||
@ -1438,6 +1613,11 @@ public final class net.corda.core.messaging.CordaRPCOpsKt extends java.lang.Obje
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.messaging.MessageRecipients
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.messaging.NodeState extends java.lang.Enum
|
||||
protected <init>(String, int)
|
||||
public static net.corda.core.messaging.NodeState valueOf(String)
|
||||
public static net.corda.core.messaging.NodeState[] values()
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.messaging.RPCOps
|
||||
public abstract int getProtocolVersion()
|
||||
##
|
||||
@ -1447,12 +1627,17 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.messaging.StateMachineInfo extends java.lang.Object
|
||||
public <init>(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed)
|
||||
public <init>(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed, net.corda.core.context.InvocationContext)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateMachineRunId component1()
|
||||
@org.jetbrains.annotations.NotNull public final String component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator component3()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.messaging.DataFeed component4()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.InvocationContext component5()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.context.InvocationContext context()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.messaging.StateMachineInfo copy(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.messaging.StateMachineInfo copy(net.corda.core.flows.StateMachineRunId, String, net.corda.core.flows.FlowInitiator, net.corda.core.messaging.DataFeed, net.corda.core.context.InvocationContext)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.context.InvocationContext getContext()
|
||||
@org.jetbrains.annotations.NotNull public final String getFlowLogicClassName()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateMachineRunId getId()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator getInitiator()
|
||||
@ -1499,6 +1684,27 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowHandle startFlow(net.corda.core.flows.FlowLogic)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NetworkParameters extends java.lang.Object
|
||||
public <init>(int, List, java.time.Duration, int, int, java.time.Instant, int)
|
||||
public final int component1()
|
||||
@org.jetbrains.annotations.NotNull public final List component2()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Duration component3()
|
||||
public final int component4()
|
||||
public final int component5()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Instant component6()
|
||||
public final int component7()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NetworkParameters copy(int, List, java.time.Duration, int, int, java.time.Instant, int)
|
||||
public boolean equals(Object)
|
||||
public final int getEpoch()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Duration getEventHorizon()
|
||||
public final int getMaxMessageSize()
|
||||
public final int getMaxTransactionSize()
|
||||
public final int getMinimumPlatformVersion()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Instant getModifiedTime()
|
||||
@org.jetbrains.annotations.NotNull public final List getNotaries()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NodeInfo extends java.lang.Object
|
||||
public <init>(List, List, int, long)
|
||||
@org.jetbrains.annotations.NotNull public final List component1()
|
||||
@ -1513,9 +1719,21 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
||||
public final int getPlatformVersion()
|
||||
public final long getSerial()
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party identityFromX500Name(net.corda.core.identity.CordaX500Name)
|
||||
public final boolean isLegalIdentity(net.corda.core.identity.Party)
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NotaryInfo extends java.lang.Object
|
||||
public <init>(net.corda.core.identity.Party, boolean)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component1()
|
||||
public final boolean component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NotaryInfo copy(net.corda.core.identity.Party, boolean)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getIdentity()
|
||||
public final boolean getValidating()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.node.ServiceHub extends net.corda.core.node.ServicesForResolution
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction addSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
|
||||
@ -1528,12 +1746,14 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.KeyManagementService getKeyManagementService()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NodeInfo getMyInfo()
|
||||
@org.jetbrains.annotations.NotNull public abstract rx.Observable getMyNodeStateObservable()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.NetworkMapCache getNetworkMapCache()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.TransactionStorage getValidatedTransactions()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.VaultService getVaultService()
|
||||
@org.jetbrains.annotations.NotNull public abstract java.sql.Connection jdbcSession()
|
||||
public abstract void recordTransactions(Iterable)
|
||||
public abstract void recordTransactions(net.corda.core.node.StatesToRecord, Iterable)
|
||||
public abstract void recordTransactions(boolean, Iterable)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction signInitialTransaction(net.corda.core.transactions.TransactionBuilder)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction signInitialTransaction(net.corda.core.transactions.TransactionBuilder, Iterable)
|
||||
@ -1548,6 +1768,11 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.node.StateLoader
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.TransactionState loadState(net.corda.core.contracts.StateRef)
|
||||
##
|
||||
public final class net.corda.core.node.StatesToRecord extends java.lang.Enum
|
||||
protected <init>(String, int)
|
||||
public static net.corda.core.node.StatesToRecord valueOf(String)
|
||||
public static net.corda.core.node.StatesToRecord[] values()
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.node.services.AttachmentStorage
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream, String, String)
|
||||
@ -1646,6 +1871,11 @@ public @interface net.corda.core.node.services.CordaService
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.ServiceHub getServices()
|
||||
public abstract void start()
|
||||
public abstract void stop()
|
||||
public static final net.corda.core.node.services.NotaryService$Companion Companion
|
||||
@org.jetbrains.annotations.NotNull public static final String ID_PREFIX = "corda.notary."
|
||||
##
|
||||
public static final class net.corda.core.node.services.NotaryService$Companion extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public final String constructId(boolean, boolean, boolean, boolean)
|
||||
##
|
||||
public abstract class net.corda.core.node.services.PartyInfo extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty()
|
||||
@ -1948,6 +2178,8 @@ public abstract class net.corda.core.node.services.vault.BaseSort extends java.l
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(reflect.Field, Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression in(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression in(kotlin.reflect.KProperty1, Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$NullExpression isNotNull()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$NullExpression isNull()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression isNull(reflect.Field)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression isNull(kotlin.reflect.KProperty1)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison lessThan(Comparable)
|
||||
@ -1956,6 +2188,7 @@ public abstract class net.corda.core.node.services.vault.BaseSort extends java.l
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison lessThanOrEqual(Comparable)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThanOrEqual(reflect.Field, Comparable)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression lessThanOrEqual(kotlin.reflect.KProperty1, Comparable)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$Likeness like(String)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(reflect.Field, String)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression like(kotlin.reflect.KProperty1, String)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$AggregateFunctionExpression max(reflect.Field)
|
||||
@ -1972,6 +2205,7 @@ public abstract class net.corda.core.node.services.vault.BaseSort extends java.l
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(reflect.Field, Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$CollectionExpression notIn(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notIn(kotlin.reflect.KProperty1, Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$Likeness notLike(String)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(reflect.Field, String)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notLike(kotlin.reflect.KProperty1, String)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.node.services.vault.CriteriaExpression$ColumnPredicateExpression notNull(reflect.Field)
|
||||
@ -2631,8 +2865,10 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.transactions.FilteredTransaction extends net.corda.core.transactions.TraversableTransaction
|
||||
public <init>(net.corda.core.crypto.SecureHash, List, List)
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.transactions.FilteredTransaction buildFilteredTransaction(net.corda.core.transactions.WireTransaction, function.Predicate)
|
||||
public final void checkAllComponentsVisible(net.corda.core.contracts.ComponentGroupEnum)
|
||||
public final void checkCommandVisibility(java.security.PublicKey)
|
||||
public final boolean checkWithFun(kotlin.jvm.functions.Function1)
|
||||
@org.jetbrains.annotations.NotNull public final List getFilteredComponentGroups()
|
||||
@org.jetbrains.annotations.NotNull public final List getGroupHashes()
|
||||
@ -2692,6 +2928,7 @@ public static final class net.corda.core.transactions.FilteredTransaction$Compan
|
||||
@org.jetbrains.annotations.NotNull public final List inputsOfType(Class)
|
||||
public String toString()
|
||||
public final void verify()
|
||||
public static final net.corda.core.transactions.LedgerTransaction$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.transactions.LedgerTransaction$InOutGroup extends java.lang.Object
|
||||
public <init>(List, List, Object)
|
||||
@ -2732,6 +2969,7 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
public void verifyRequiredSignatures()
|
||||
public void verifySignaturesExcept(Collection)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.transactions.NotaryChangeWireTransaction extends net.corda.core.transactions.CoreTransaction
|
||||
public <init>(List, net.corda.core.identity.Party, net.corda.core.identity.Party)
|
||||
@ -2747,6 +2985,7 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
||||
public int hashCode()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.ServiceHub, List)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.StateLoader, List)
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.transactions.SignedTransaction extends java.lang.Object implements net.corda.core.transactions.TransactionWithSignatures
|
||||
@ -2772,13 +3011,17 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
||||
public final boolean isNotaryChangeTransaction()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction plus(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction plus(net.corda.core.crypto.TransactionSignature)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.BaseTransaction resolveBaseTransaction(net.corda.core.node.StateLoader)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolveNotaryChangeTransaction(net.corda.core.node.ServiceHub)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolveNotaryChangeTransaction(net.corda.core.node.StateLoader)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.TransactionWithSignatures resolveTransactionWithSignatures(net.corda.core.node.ServiceHub)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction toLedgerTransaction(net.corda.core.node.ServiceHub)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction toLedgerTransaction(net.corda.core.node.ServiceHub, boolean)
|
||||
@org.jetbrains.annotations.NotNull public String toString()
|
||||
public final void verify(net.corda.core.node.ServiceHub)
|
||||
public final void verify(net.corda.core.node.ServiceHub, boolean)
|
||||
public void verifyRequiredSignatures()
|
||||
public void verifySignaturesExcept(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(java.security.KeyPair, net.corda.core.crypto.SignatureMetadata)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignature(net.corda.core.crypto.TransactionSignature)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable)
|
||||
@ -2842,6 +3085,7 @@ public class net.corda.core.transactions.TransactionBuilder extends java.lang.Ob
|
||||
@org.jetbrains.annotations.NotNull public abstract Set getRequiredSigningKeys()
|
||||
@org.jetbrains.annotations.NotNull public abstract List getSigs()
|
||||
public abstract void verifyRequiredSignatures()
|
||||
public abstract void verifySignaturesExcept(Collection)
|
||||
##
|
||||
@net.corda.core.DoNotImplement public abstract class net.corda.core.transactions.TraversableTransaction extends net.corda.core.transactions.CoreTransaction
|
||||
public <init>(List)
|
||||
@ -2919,6 +3163,21 @@ public final class net.corda.core.utilities.EncodingUtils extends java.lang.Obje
|
||||
@org.jetbrains.annotations.NotNull public static final String toBase64(byte[])
|
||||
@org.jetbrains.annotations.NotNull public static final String toHex(byte[])
|
||||
@org.jetbrains.annotations.NotNull public static final byte[] toSHA256Bytes(java.security.PublicKey)
|
||||
public static final int MAX_HASH_HEX_SIZE = 130
|
||||
##
|
||||
public class net.corda.core.utilities.Id extends java.lang.Object
|
||||
public <init>(Object, String, java.time.Instant)
|
||||
public final boolean equals(Object)
|
||||
@org.jetbrains.annotations.Nullable public final String getEntityType()
|
||||
@org.jetbrains.annotations.NotNull public final java.time.Instant getTimestamp()
|
||||
@org.jetbrains.annotations.NotNull public final Object getValue()
|
||||
public final int hashCode()
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public static final net.corda.core.utilities.Id newInstance(Object, String, java.time.Instant)
|
||||
@org.jetbrains.annotations.NotNull public final String toString()
|
||||
public static final net.corda.core.utilities.Id$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.utilities.Id$Companion extends java.lang.Object
|
||||
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.utilities.Id newInstance(Object, String, java.time.Instant)
|
||||
##
|
||||
public final class net.corda.core.utilities.KotlinUtilsKt extends java.lang.Object
|
||||
public static final void debug(org.slf4j.Logger, kotlin.jvm.functions.Function0)
|
||||
@ -3120,6 +3379,13 @@ public static interface net.corda.core.utilities.UntrustworthyData$Validator ext
|
||||
public final class net.corda.core.utilities.UntrustworthyDataKt extends java.lang.Object
|
||||
public static final Object unwrap(net.corda.core.utilities.UntrustworthyData, kotlin.jvm.functions.Function1)
|
||||
##
|
||||
public final class net.corda.core.utilities.UuidGenerator extends java.lang.Object
|
||||
public <init>()
|
||||
public static final net.corda.core.utilities.UuidGenerator$Companion Companion
|
||||
##
|
||||
public static final class net.corda.core.utilities.UuidGenerator$Companion extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public final UUID next()
|
||||
##
|
||||
public interface net.corda.core.utilities.VariablePropertyDelegate extends net.corda.core.utilities.PropertyDelegate
|
||||
public abstract void setValue(Object, kotlin.reflect.KProperty, Object)
|
||||
##
|
||||
@ -3295,6 +3561,7 @@ public final class net.corda.client.rpc.CordaRPCClient extends java.lang.Object
|
||||
public <init>(net.corda.core.utilities.NetworkHostAndPort)
|
||||
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCConnection start(String, String)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.client.rpc.CordaRPCConnection start(String, String, net.corda.core.context.Trace, net.corda.core.context.Actor)
|
||||
public final Object use(String, String, kotlin.jvm.functions.Function1)
|
||||
##
|
||||
public final class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.Object
|
||||
|
@ -6,11 +6,11 @@ import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.bufferUntilSubscribed
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
@ -148,8 +148,8 @@ class NodeMonitorModelTest {
|
||||
// ISSUE
|
||||
expect { add: StateMachineUpdate.Added ->
|
||||
issueSmId = add.id
|
||||
val initiator = add.stateMachineInfo.initiator
|
||||
require(initiator is FlowInitiator.RPC && initiator.username == "user1")
|
||||
val context = add.stateMachineInfo.context()
|
||||
require(context.origin is Origin.RPC && context.principal().name == "user1")
|
||||
},
|
||||
expect { remove: StateMachineUpdate.Removed ->
|
||||
require(remove.id == issueSmId)
|
||||
@ -157,8 +157,8 @@ class NodeMonitorModelTest {
|
||||
// MOVE - N.B. There are other framework flows that happen in parallel for the remote resolve transactions flow
|
||||
expect(match = { it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added ->
|
||||
moveSmId = add.id
|
||||
val initiator = add.stateMachineInfo.initiator
|
||||
require(initiator is FlowInitiator.RPC && initiator.username == "user1")
|
||||
val context = add.stateMachineInfo.context()
|
||||
require(context.origin is Origin.RPC && context.principal().name == "user1")
|
||||
},
|
||||
expect(match = { it is StateMachineUpdate.Removed && it.id == moveSmId }) {
|
||||
}
|
||||
@ -169,8 +169,8 @@ class NodeMonitorModelTest {
|
||||
sequence(
|
||||
// MOVE
|
||||
expect { add: StateMachineUpdate.Added ->
|
||||
val initiator = add.stateMachineInfo.initiator
|
||||
require(initiator is FlowInitiator.Peer && aliceNode.isLegalIdentity(initiator.party))
|
||||
val context = add.stateMachineInfo.context()
|
||||
require(context.origin is Origin.Peer && aliceNode.isLegalIdentity(aliceNode.identityFromX500Name((context.origin as Origin.Peer).party)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import net.corda.core.context.*
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.messaging.*
|
||||
@ -20,14 +20,15 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
@ -44,8 +45,8 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
private lateinit var client: CordaRPCClient
|
||||
private var connection: CordaRPCConnection? = null
|
||||
|
||||
private fun login(username: String, password: String) {
|
||||
connection = client.start(username, password)
|
||||
private fun login(username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null) {
|
||||
connection = client.start(username, password, externalTrace, impersonatedActor)
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -131,31 +132,51 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
|
||||
@Test
|
||||
fun `flow initiator via RPC`() {
|
||||
login(rpcUser.username, rpcUser.password)
|
||||
val externalTrace = Trace.newInstance()
|
||||
val impersonatedActor = Actor(Actor.Id("Mark Dadada"), AuthServiceId("Test"), owningLegalIdentity = BOB.name)
|
||||
login(rpcUser.username, rpcUser.password, externalTrace, impersonatedActor)
|
||||
val proxy = connection!!.proxy
|
||||
var countRpcFlows = 0
|
||||
var countShellFlows = 0
|
||||
proxy.stateMachinesFeed().updates.subscribe {
|
||||
if (it is StateMachineUpdate.Added) {
|
||||
val initiator = it.stateMachineInfo.initiator
|
||||
if (initiator is FlowInitiator.RPC)
|
||||
countRpcFlows++
|
||||
if (initiator is FlowInitiator.Shell)
|
||||
countShellFlows++
|
||||
}
|
||||
}
|
||||
val nodeIdentity = node.info.chooseIdentity()
|
||||
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), nodeIdentity), FlowInitiator.Shell).flatMap { it.resultFuture }.getOrThrow()
|
||||
proxy.startFlow(::CashIssueFlow,
|
||||
123.DOLLARS,
|
||||
OpaqueBytes.of(0),
|
||||
nodeIdentity
|
||||
).returnValue.getOrThrow()
|
||||
proxy.startFlowDynamic(CashIssueFlow::class.java,
|
||||
1000.DOLLARS,
|
||||
OpaqueBytes.of(0),
|
||||
nodeIdentity).returnValue.getOrThrow()
|
||||
assertEquals(2, countRpcFlows)
|
||||
assertEquals(1, countShellFlows)
|
||||
|
||||
val updates = proxy.stateMachinesFeed().updates
|
||||
|
||||
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), nodeIdentity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||
proxy.startFlow(::CashIssueFlow, 123.DOLLARS, OpaqueBytes.of(0), nodeIdentity).returnValue.getOrThrow()
|
||||
proxy.startFlowDynamic(CashIssueFlow::class.java, 1000.DOLLARS, OpaqueBytes.of(0), nodeIdentity).returnValue.getOrThrow()
|
||||
|
||||
val historicalIds = mutableSetOf<Trace.InvocationId>()
|
||||
var sessionId: Trace.SessionId? = null
|
||||
updates.expectEvents(isStrict = false) {
|
||||
sequence(
|
||||
expect { update: StateMachineUpdate.Added ->
|
||||
checkShellNotification(update.stateMachineInfo)
|
||||
},
|
||||
expect { update: StateMachineUpdate.Added ->
|
||||
checkRpcNotification(update.stateMachineInfo, rpcUser.username, historicalIds, externalTrace, impersonatedActor)
|
||||
sessionId = update.stateMachineInfo.context().trace.sessionId
|
||||
},
|
||||
expect { update: StateMachineUpdate.Added ->
|
||||
checkRpcNotification(update.stateMachineInfo, rpcUser.username, historicalIds, externalTrace, impersonatedActor)
|
||||
assertThat(update.stateMachineInfo.context().trace.sessionId).isEqualTo(sessionId)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkShellNotification(info: StateMachineInfo) {
|
||||
|
||||
val context = info.context()
|
||||
assertThat(context.origin).isInstanceOf(Origin.Shell::class.java)
|
||||
}
|
||||
|
||||
private fun checkRpcNotification(info: StateMachineInfo, rpcUsername: String, historicalIds: MutableSet<Trace.InvocationId>, externalTrace: Trace?, impersonatedActor: Actor?) {
|
||||
|
||||
val context = info.context()
|
||||
assertThat(context.origin).isInstanceOf(Origin.RPC::class.java)
|
||||
assertThat(context.externalTrace).isEqualTo(externalTrace)
|
||||
assertThat(context.impersonatedActor).isEqualTo(impersonatedActor)
|
||||
assertThat(context.actor?.id?.value).isEqualTo(rpcUsername)
|
||||
assertThat(historicalIds).doesNotContain(context.trace.invocationId)
|
||||
historicalIds.add(context.trace.invocationId)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.corda.client.rpc
|
||||
|
||||
import net.corda.client.rpc.internal.RPCClient
|
||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
@ -348,9 +349,10 @@ class RPCStabilityTests {
|
||||
val message = session.createMessage(false)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(
|
||||
clientAddress = SimpleString(myQueue),
|
||||
id = RPCApi.RpcRequestId(random63BitValue()),
|
||||
methodName = SlowConsumerRPCOps::streamAtInterval.name,
|
||||
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT).bytes
|
||||
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT).bytes,
|
||||
replyId = Trace.InvocationId.newInstance(),
|
||||
sessionId = Trace.SessionId.newInstance()
|
||||
)
|
||||
request.writeToClientMessage(message)
|
||||
producer.send(message)
|
||||
|
@ -3,6 +3,8 @@ package net.corda.client.rpc
|
||||
import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||
import net.corda.client.rpc.internal.RPCClient
|
||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
@ -99,7 +101,22 @@ class CordaRPCClient @JvmOverloads constructor(
|
||||
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
|
||||
*/
|
||||
fun start(username: String, password: String): CordaRPCConnection {
|
||||
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password))
|
||||
return start(username, password, null, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the target server and returns an active connection. The returned connection is a [java.io.Closeable]
|
||||
* and can be used with a try-with-resources statement. If you don't use that, you should use the
|
||||
* [RPCConnection.notifyServerAndClose] or [RPCConnection.forceClose] methods to dispose of the connection object
|
||||
* when done.
|
||||
*
|
||||
* @param username The username to authenticate with.
|
||||
* @param password The password to authenticate with.
|
||||
* @param externalTrace external [Trace] for correlation.
|
||||
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
|
||||
*/
|
||||
fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?): CordaRPCConnection {
|
||||
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password, externalTrace, impersonatedActor))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,8 @@ package net.corda.client.rpc.internal
|
||||
|
||||
import net.corda.client.rpc.RPCConnection
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.logElapsedTime
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
@ -100,7 +102,9 @@ class RPCClient<I : RPCOps>(
|
||||
fun start(
|
||||
rpcOpsClass: Class<I>,
|
||||
username: String,
|
||||
password: String
|
||||
password: String,
|
||||
externalTrace: Trace? = null,
|
||||
impersonatedActor: Actor? = null
|
||||
): RPCConnection<I> {
|
||||
return log.logElapsedTime("Startup") {
|
||||
val clientAddress = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.${random63BitValue()}")
|
||||
@ -113,8 +117,8 @@ class RPCClient<I : RPCOps>(
|
||||
minLargeMessageSize = rpcConfiguration.maxFileSize
|
||||
isUseGlobalPools = nodeSerializationEnv != null
|
||||
}
|
||||
|
||||
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext)
|
||||
val sessionId = Trace.SessionId.newInstance()
|
||||
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext, sessionId, externalTrace, impersonatedActor)
|
||||
try {
|
||||
proxyHandler.start()
|
||||
val ops: I = uncheckedCast(Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), proxyHandler))
|
||||
|
@ -12,7 +12,9 @@ import com.google.common.util.concurrent.SettableFuture
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.client.rpc.RPCSinceVersion
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.context.Trace.InvocationId
|
||||
import net.corda.core.internal.LazyPool
|
||||
import net.corda.core.internal.LazyStickyPool
|
||||
import net.corda.core.internal.LifeCycle
|
||||
@ -24,7 +26,9 @@ import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisConsumer
|
||||
import net.corda.nodeapi.ArtemisProducer
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||
@ -35,6 +39,7 @@ import rx.Observable
|
||||
import rx.subjects.UnicastSubject
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
@ -70,7 +75,10 @@ class RPCClientProxyHandler(
|
||||
private val serverLocator: ServerLocator,
|
||||
private val clientAddress: SimpleString,
|
||||
private val rpcOpsClass: Class<out RPCOps>,
|
||||
serializationContext: SerializationContext
|
||||
serializationContext: SerializationContext,
|
||||
private val sessionId: Trace.SessionId,
|
||||
private val externalTrace: Trace?,
|
||||
private val impersonatedActor: Actor?
|
||||
) : InvocationHandler {
|
||||
|
||||
private enum class State {
|
||||
@ -127,14 +135,14 @@ class RPCClientProxyHandler(
|
||||
|
||||
// Stores the Observable IDs that are already removed from the map but are not yet sent to the server.
|
||||
private val observablesToReap = ThreadBox(object {
|
||||
var observables = ArrayList<RPCApi.ObservableId>()
|
||||
var observables = ArrayList<InvocationId>()
|
||||
})
|
||||
private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext)
|
||||
|
||||
private fun createRpcObservableMap(): RpcObservableMap {
|
||||
val onObservableRemove = RemovalListener<RPCApi.ObservableId, UnicastSubject<Notification<*>>> {
|
||||
val onObservableRemove = RemovalListener<InvocationId, UnicastSubject<Notification<*>>> {
|
||||
val observableId = it.key!!
|
||||
val rpcCallSite = callSiteMap?.remove(observableId.toLong)
|
||||
val rpcCallSite = callSiteMap?.remove(observableId)
|
||||
if (it.cause == RemovalCause.COLLECTED) {
|
||||
log.warn(listOf(
|
||||
"A hot observable returned from an RPC was never subscribed to.",
|
||||
@ -207,11 +215,12 @@ class RPCClientProxyHandler(
|
||||
if (sessionAndConsumer!!.session.isClosed) {
|
||||
throw RPCException("RPC Proxy is closed")
|
||||
}
|
||||
val rpcId = RPCApi.RpcRequestId(random63BitValue())
|
||||
callSiteMap?.set(rpcId.toLong, Throwable("<Call site of root RPC '${method.name}'>"))
|
||||
|
||||
val replyId = InvocationId.newInstance()
|
||||
callSiteMap?.set(replyId, Throwable("<Call site of root RPC '${method.name}'>"))
|
||||
try {
|
||||
val serialisedArguments = (arguments?.toList() ?: emptyList()).serialize(context = serializationContextWithObservableContext)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(clientAddress, rpcId, method.name, serialisedArguments.bytes)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(clientAddress, method.name, serialisedArguments.bytes, replyId, sessionId, externalTrace, impersonatedActor)
|
||||
val replyFuture = SettableFuture.create<Any>()
|
||||
sessionAndProducerPool.run {
|
||||
val message = it.session.createMessage(false)
|
||||
@ -219,11 +228,11 @@ class RPCClientProxyHandler(
|
||||
|
||||
log.debug {
|
||||
val argumentsString = arguments?.joinToString() ?: ""
|
||||
"-> RPC($rpcId) -> ${method.name}($argumentsString): ${method.returnType}"
|
||||
"-> RPC(${replyId.value}) -> ${method.name}($argumentsString): ${method.returnType}"
|
||||
}
|
||||
|
||||
require(rpcReplyMap.put(rpcId, replyFuture) == null) {
|
||||
"Generated several RPC requests with same ID $rpcId"
|
||||
require(rpcReplyMap.put(replyId, replyFuture) == null) {
|
||||
"Generated several RPC requests with same ID $replyId"
|
||||
}
|
||||
it.producer.send(message)
|
||||
it.session.commit()
|
||||
@ -236,7 +245,7 @@ class RPCClientProxyHandler(
|
||||
// This must be a checked exception, so wrap it
|
||||
throw RPCException(e.message ?: "", e)
|
||||
} finally {
|
||||
callSiteMap?.remove(rpcId.toLong)
|
||||
callSiteMap?.remove(replyId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +263,7 @@ class RPCClientProxyHandler(
|
||||
when (result) {
|
||||
is Try.Success -> replyFuture.set(result.value)
|
||||
is Try.Failure -> {
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong)
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id)
|
||||
if (rpcCallSite != null) addRpcCallSiteToThrowable(result.exception, rpcCallSite)
|
||||
replyFuture.setException(result.exception)
|
||||
}
|
||||
@ -277,7 +286,7 @@ class RPCClientProxyHandler(
|
||||
}
|
||||
// Add call site information on error
|
||||
if (content.isOnError) {
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong)
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id)
|
||||
if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite)
|
||||
}
|
||||
observable.onNext(content)
|
||||
@ -385,9 +394,9 @@ class RPCClientProxyHandler(
|
||||
}
|
||||
}
|
||||
|
||||
private typealias RpcObservableMap = Cache<RPCApi.ObservableId, UnicastSubject<Notification<*>>>
|
||||
private typealias RpcReplyMap = ConcurrentHashMap<RPCApi.RpcRequestId, SettableFuture<Any?>>
|
||||
private typealias CallSiteMap = ConcurrentHashMap<Long, Throwable?>
|
||||
private typealias RpcObservableMap = Cache<InvocationId, UnicastSubject<Notification<*>>>
|
||||
private typealias RpcReplyMap = ConcurrentHashMap<InvocationId, SettableFuture<Any?>>
|
||||
private typealias CallSiteMap = ConcurrentHashMap<InvocationId, Throwable?>
|
||||
|
||||
/**
|
||||
* Holds a context available during Kryo deserialisation of messages that are expected to contain Observables.
|
||||
@ -426,14 +435,14 @@ object RpcClientObservableSerializer : Serializer<Observable<*>>() {
|
||||
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<Observable<*>>): Observable<Any> {
|
||||
val observableContext = kryo.context[RpcObservableContextKey] as ObservableContext
|
||||
val observableId = RPCApi.ObservableId(input.readLong(true))
|
||||
val observableId = input.readInvocationId() ?: throw IllegalStateException("Unable to read invocationId from Input.")
|
||||
val observable = UnicastSubject.create<Notification<*>>()
|
||||
require(observableContext.observableMap.getIfPresent(observableId) == null) {
|
||||
"Multiple Observables arrived with the same ID $observableId"
|
||||
}
|
||||
val rpcCallSite = getRpcCallSite(kryo, observableContext)
|
||||
observableContext.observableMap.put(observableId, observable)
|
||||
observableContext.callSiteMap?.put(observableId.toLong, rpcCallSite)
|
||||
observableContext.callSiteMap?.put(observableId, rpcCallSite)
|
||||
// We pin all Observables into a hard reference store (rooted in the RPC proxy) on subscription so that users
|
||||
// don't need to store a reference to the Observables themselves.
|
||||
return pinInSubscriptions(observable, observableContext.hardReferenceStore).doOnUnsubscribe {
|
||||
@ -444,12 +453,19 @@ object RpcClientObservableSerializer : Serializer<Observable<*>>() {
|
||||
}.dematerialize()
|
||||
}
|
||||
|
||||
private fun Input.readInvocationId() : InvocationId? {
|
||||
|
||||
val value = readString() ?: return null
|
||||
val timestamp = readLong()
|
||||
return InvocationId(value, Instant.ofEpochMilli(timestamp))
|
||||
}
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, observable: Observable<*>) {
|
||||
throw UnsupportedOperationException("Cannot serialise Observables on the client side")
|
||||
}
|
||||
|
||||
private fun getRpcCallSite(kryo: Kryo, observableContext: ObservableContext): Throwable? {
|
||||
val rpcRequestOrObservableId = kryo.context[RPCApi.RpcRequestOrObservableIdKey] as Long
|
||||
val rpcRequestOrObservableId = kryo.context[RPCApi.RpcRequestOrObservableIdKey] as InvocationId
|
||||
return observableContext.callSiteMap?.get(rpcRequestOrObservableId)
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
|
||||
|
||||
inner class TestOpsImpl : TestOps {
|
||||
override val protocolVersion = 1
|
||||
// do not remove Unit
|
||||
override fun barf(): Unit = throw IllegalArgumentException("Barf!")
|
||||
override fun void() {}
|
||||
override fun someCalculation(str: String, num: Int) = "$str $num"
|
||||
@ -64,8 +65,9 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
|
||||
override fun makeListenableFuture() = doneFuture(1)
|
||||
override fun makeComplicatedObservable() = complicatedObservable
|
||||
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
|
||||
// do not remove Unit
|
||||
override fun addedLater(): Unit = throw IllegalStateException()
|
||||
override fun captureUser(): String = rpcContext().currentUser.username
|
||||
override fun captureUser(): String = rpcContext().invocation.principal().name
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4,7 +4,6 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.messaging.rpcContext
|
||||
import net.corda.node.services.messaging.requirePermission
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
|
@ -14,11 +14,8 @@ import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.CHARLIE_NAME
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
<Appenders>
|
||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%highlight{%level{length=1} %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n}{INFO=white,WARN=red,FATAL=bright red}" />
|
||||
<PatternLayout pattern="%highlight{%level{length=1} %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n}{INFO=white,WARN=red,FATAL=bright red}" />
|
||||
</Console>
|
||||
|
||||
<!-- Required for printBasicInfo -->
|
||||
@ -27,7 +27,7 @@
|
||||
fileName="${sys:log-path}/${log-name}.log"
|
||||
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||
|
||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
|
||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
||||
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
|
@ -0,0 +1,9 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
/**
|
||||
* Authentication / Authorisation Service ID.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class AuthServiceId(val value: String)
|
136
core/src/main/kotlin/net/corda/core/context/InvocationContext.kt
Normal file
136
core/src/main/kotlin/net/corda/core/context/InvocationContext.kt
Normal file
@ -0,0 +1,136 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.contracts.ScheduledStateRef
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.security.Principal
|
||||
|
||||
/**
|
||||
* Models the information needed to trace an invocation in Corda.
|
||||
* Includes initiating actor, origin, trace information, and optional external trace information to correlate clients' IDs.
|
||||
*
|
||||
* @param origin origin of the invocation.
|
||||
* @param trace Corda invocation trace.
|
||||
* @param actor acting agent of the invocation, used to derive the security principal.
|
||||
* @param externalTrace optional external invocation trace for cross-system logs correlation.
|
||||
* @param impersonatedActor optional impersonated actor, used for logging but not for authorisation.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class InvocationContext(val origin: Origin, val trace: Trace, val actor: Actor?, val externalTrace: Trace? = null, val impersonatedActor: Actor? = null) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(origin: Origin, trace: Trace = Trace.newInstance(), actor: Actor? = null, externalTrace: Trace? = null, impersonatedActor: Actor? = null) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.RPC] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun rpc(actor: Actor, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.RPC(actor), trace, actor, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Peer] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.Peer(party), trace, null, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Service] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Scheduled] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Scheduled(scheduledState), trace, null, externalTrace)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Shell] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(Origin.Shell, trace, null, externalTrace)
|
||||
}
|
||||
|
||||
/**
|
||||
* Associated security principal.
|
||||
*/
|
||||
fun principal(): Principal = origin.principal()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models an initiator in Corda, can be a user, a service, etc.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdentity: CordaX500Name) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name): Actor = Actor(Id(serviceClassName), AuthServiceId("SERVICE"), owningLegalIdentity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor id.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Id(val value: String)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invocation origin for tracing purposes.
|
||||
*/
|
||||
@CordaSerializable
|
||||
sealed class Origin {
|
||||
|
||||
/**
|
||||
* Returns the [Principal] for a given [Actor].
|
||||
*/
|
||||
abstract fun principal(): Principal
|
||||
|
||||
/**
|
||||
* Origin was an RPC call.
|
||||
*/
|
||||
data class RPC(private val actor: Actor) : Origin() {
|
||||
|
||||
override fun principal() = Principal { actor.id.value }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a message sent by a [Peer].
|
||||
*/
|
||||
data class Peer(val party: CordaX500Name) : Origin() {
|
||||
|
||||
override fun principal() = Principal { party.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a Corda Service.
|
||||
*/
|
||||
data class Service(val serviceClassName: String, val owningLegalIdentity: CordaX500Name) : Origin() {
|
||||
|
||||
override fun principal() = Principal { serviceClassName }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a scheduled activity.
|
||||
*/
|
||||
data class Scheduled(val scheduledState: ScheduledStateRef) : Origin() {
|
||||
|
||||
override fun principal() = Principal { "Scheduler" }
|
||||
}
|
||||
|
||||
// TODO When proper ssh access enabled, add username/use RPC?
|
||||
/**
|
||||
* Origin was the Shell.
|
||||
*/
|
||||
object Shell : Origin() {
|
||||
|
||||
override fun principal() = Principal { "Shell User" }
|
||||
}
|
||||
}
|
56
core/src/main/kotlin/net/corda/core/context/Trace.kt
Normal file
56
core/src/main/kotlin/net/corda/core/context/Trace.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.Id
|
||||
import net.corda.core.utilities.UuidGenerator
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Contextual tracing information, including invocation and session id.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Trace(val invocationId: InvocationId, val sessionId: SessionId) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a trace using a [InvocationId.newInstance] with default arguments and a [SessionId] matching the value and timestamp from the invocation id..
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(invocationId: InvocationId = InvocationId.newInstance(), sessionId: SessionId = SessionId(invocationId.value, invocationId.timestamp)) = Trace(invocationId, sessionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents id and timestamp of an invocation.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class InvocationId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
|
||||
|
||||
companion object {
|
||||
private val TYPE = "Invocation"
|
||||
|
||||
/**
|
||||
* Creates an invocation id using a [java.util.UUID] as value and [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = InvocationId(value, timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents id and timestamp of a session.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class SessionId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
|
||||
|
||||
companion object {
|
||||
private val TYPE = "Session"
|
||||
|
||||
/**
|
||||
* Creates a session id using a [java.util.UUID] as value and [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = SessionId(value, timestamp)
|
||||
}
|
||||
}
|
||||
}
|
@ -86,6 +86,7 @@ interface Scheduled {
|
||||
* This is effectively the input to a scheduler, which wakes up at that point in time and asks the contract state what
|
||||
* lifecycle processing needs to take place. e.g. a fixing or a late payment etc.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instant) : Scheduled
|
||||
|
||||
/**
|
||||
|
@ -10,29 +10,35 @@ import java.security.Principal
|
||||
* communication started by peer node [FlowInitiator.Peer], scheduled flows [FlowInitiator.Scheduled]
|
||||
* or via the Corda Shell [FlowInitiator.Shell].
|
||||
*/
|
||||
@Deprecated("Do not use these types. Future releases might remove them.")
|
||||
@CordaSerializable
|
||||
sealed class FlowInitiator : Principal {
|
||||
/** Started using [net.corda.core.messaging.CordaRPCOps.startFlowDynamic]. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class RPC(val username: String) : FlowInitiator() {
|
||||
override fun getName(): String = username
|
||||
}
|
||||
|
||||
/** Started when we get new session initiation request. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Peer(val party: Party) : FlowInitiator() {
|
||||
override fun getName(): String = party.name.toString()
|
||||
}
|
||||
|
||||
/** Started by a CordaService. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Service(val serviceClassName: String) : FlowInitiator() {
|
||||
override fun getName(): String = serviceClassName
|
||||
}
|
||||
|
||||
/** Started as scheduled activity. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() {
|
||||
override fun getName(): String = "Scheduler"
|
||||
}
|
||||
|
||||
// TODO When proper ssh access enabled, add username/use RPC?
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
object Shell : FlowInitiator() {
|
||||
override fun getName(): String = "Shell User"
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
@ -55,7 +56,7 @@ interface FlowStateMachine<R> {
|
||||
val logger: Logger
|
||||
val id: StateMachineRunId
|
||||
val resultFuture: CordaFuture<R>
|
||||
val flowInitiator: FlowInitiator
|
||||
val context: InvocationContext
|
||||
val ourIdentityAndCert: PartyAndCertificate
|
||||
|
||||
@Suspendable
|
||||
|
@ -1,6 +1,10 @@
|
||||
package net.corda.core.messaging
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
@ -23,13 +27,41 @@ import java.io.InputStream
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
|
||||
private val unknownName = CordaX500Name("UNKNOWN", "UNKNOWN", "GB")
|
||||
|
||||
@CordaSerializable
|
||||
data class StateMachineInfo(
|
||||
data class StateMachineInfo @JvmOverloads constructor(
|
||||
val id: StateMachineRunId,
|
||||
val flowLogicClassName: String,
|
||||
val initiator: FlowInitiator,
|
||||
val progressTrackerStepAndUpdates: DataFeed<String, String>?
|
||||
val progressTrackerStepAndUpdates: DataFeed<String, String>?,
|
||||
val context: InvocationContext? = null
|
||||
) {
|
||||
fun context(): InvocationContext = context ?: contextFrom(initiator)
|
||||
|
||||
private fun contextFrom(initiator: FlowInitiator): InvocationContext {
|
||||
var actor: Actor? = null
|
||||
val origin: Origin
|
||||
when (initiator) {
|
||||
is FlowInitiator.RPC -> {
|
||||
actor = Actor(Actor.Id(initiator.username), AuthServiceId("UNKNOWN"), unknownName)
|
||||
origin = Origin.RPC(actor)
|
||||
}
|
||||
is FlowInitiator.Peer -> origin = Origin.Peer(initiator.party.name)
|
||||
is FlowInitiator.Service -> origin = Origin.Service(initiator.serviceClassName, unknownName)
|
||||
is FlowInitiator.Shell -> origin = Origin.Shell
|
||||
is FlowInitiator.Scheduled -> origin = Origin.Scheduled(initiator.scheduledState)
|
||||
}
|
||||
return InvocationContext.newInstance(origin = origin, actor = actor)
|
||||
}
|
||||
|
||||
fun copy(id: StateMachineRunId = this.id,
|
||||
flowLogicClassName: String = this.flowLogicClassName,
|
||||
initiator: FlowInitiator = this.initiator,
|
||||
progressTrackerStepAndUpdates: DataFeed<String, String>? = this.progressTrackerStepAndUpdates): StateMachineInfo {
|
||||
return copy(id = id, flowLogicClassName = flowLogicClassName, initiator = initiator, progressTrackerStepAndUpdates = progressTrackerStepAndUpdates, context = context)
|
||||
}
|
||||
|
||||
override fun toString(): String = "${javaClass.simpleName}($id, $flowLogicClassName)"
|
||||
}
|
||||
|
||||
@ -221,7 +253,7 @@ interface CordaRPCOps : RPCOps {
|
||||
fun uploadAttachment(jar: InputStream): SecureHash
|
||||
|
||||
/** Uploads a jar including metadata to the node, returns it's hash. */
|
||||
fun uploadAttachmentWithMetadata(jar: InputStream, uploader:String, filename:String): SecureHash
|
||||
fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash
|
||||
|
||||
/** Queries attachments metadata */
|
||||
fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId>
|
||||
|
46
core/src/main/kotlin/net/corda/core/utilities/Id.kt
Normal file
46
core/src/main/kotlin/net/corda/core/utilities/Id.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package net.corda.core.utilities
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.Instant.now
|
||||
|
||||
/**
|
||||
* Represents a unique, timestamped id.
|
||||
* @param value unique value of the id.
|
||||
* @param entityType optional id entity type.
|
||||
* @param timestamp timestamp for the id.
|
||||
*/
|
||||
open class Id<out VALUE : Any>(val value: VALUE, val entityType: String?, val timestamp: Instant) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an id using [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <V : Any> newInstance(value: V, entityType: String? = null, timestamp: Instant = now()) = Id(value, entityType, timestamp)
|
||||
}
|
||||
|
||||
final override fun equals(other: Any?): Boolean {
|
||||
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Id<*>
|
||||
|
||||
if (value != other.value) return false
|
||||
if (entityType != other.entityType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
final override fun hashCode(): Int {
|
||||
|
||||
var result = value.hashCode()
|
||||
result = 31 * result + (entityType?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
final override fun toString(): String {
|
||||
|
||||
return "$value, timestamp: $timestamp" + (entityType?.let { ", entityType: $it" } ?: "")
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package net.corda.core.utilities
|
||||
|
||||
import java.util.*
|
||||
|
||||
class UuidGenerator {
|
||||
|
||||
companion object {
|
||||
fun next() : UUID = UUID.randomUUID()
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import java.util.concurrent.Future;
|
||||
import static net.corda.testing.CoreTestUtils.chooseIdentity;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static net.corda.testing.NodeTestUtils.startFlow;
|
||||
|
||||
public class FlowsInJavaTest {
|
||||
private final MockNetwork mockNet = new MockNetwork();
|
||||
@ -36,7 +37,7 @@ public class FlowsInJavaTest {
|
||||
@Test
|
||||
public void suspendableActionInsideUnwrap() throws Exception {
|
||||
bobNode.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class);
|
||||
Future<String> result = aliceNode.getServices().startFlow(new SendInUnwrapFlow(chooseIdentity(bobNode.getInfo()))).getResultFuture();
|
||||
Future<String> result = startFlow(aliceNode.getServices(), new SendInUnwrapFlow(chooseIdentity(bobNode.getInfo()))).getResultFuture();
|
||||
mockNet.runNetwork();
|
||||
assertThat(result.get()).isEqualTo("Hello");
|
||||
}
|
||||
@ -52,7 +53,7 @@ public class FlowsInJavaTest {
|
||||
|
||||
private void primitiveReceiveTypeTest(Class<?> receiveType) throws InterruptedException {
|
||||
PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(chooseIdentity(bobNode.getInfo()), receiveType);
|
||||
Future<?> result = aliceNode.getServices().startFlow(flow).getResultFuture();
|
||||
Future<?> result = startFlow(aliceNode.getServices(), flow).getResultFuture();
|
||||
mockNet.runNetwork();
|
||||
try {
|
||||
result.get();
|
||||
|
@ -10,12 +10,9 @@ import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -7,11 +7,8 @@ import net.corda.finance.POUNDS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.issuedBy
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.CHARLIE
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.network
|
||||
import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -13,6 +13,7 @@ import net.corda.testing.MINI_CORP
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -18,6 +18,7 @@ import net.corda.node.utilities.currentDBSession
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -19,6 +19,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.node.MessagingServiceSpy
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.setMessagingServiceSpy
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
|
@ -10,6 +10,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
|
@ -10,6 +10,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -10,8 +10,7 @@ import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -33,7 +33,8 @@ class ConfigHolder(services: AppServiceHub) : SingletonSerializeAsToken() {
|
||||
// Warning!! You are about to see a major hack!
|
||||
val baseDirectory = services.declaredField<Any>("serviceHub").value
|
||||
.let { it.javaClass.getMethod("getConfiguration").apply { isAccessible = true }.invoke(it) }
|
||||
.declaredField<Path>("baseDirectory").value
|
||||
.let { it.javaClass.getMethod("getBaseDirectory").apply { isAccessible = true }.invoke(it)}
|
||||
.let { it.javaClass.getMethod("toString").apply { isAccessible = true }.invoke(it) as String }
|
||||
val config = (baseDirectory / "node.conf").read { ConfigFactory.parseReader(it.reader()) }
|
||||
if (config.hasPath("issuableCurrencies")) {
|
||||
issuableCurrencies = config.getStringList("issuableCurrencies").map { Currency.getInstance(it) }
|
||||
|
@ -6,6 +6,7 @@ import net.corda.finance.flows.CashException
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
|
@ -12,6 +12,7 @@ import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -12,6 +12,7 @@ import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -1,15 +1,24 @@
|
||||
package net.corda.nodeapi
|
||||
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.AuthServiceId
|
||||
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.serialization.SerializationContext
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.Id
|
||||
import net.corda.core.utilities.Try
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQBuffer
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.*
|
||||
import org.apache.activemq.artemis.api.core.management.CoreNotificationType
|
||||
import org.apache.activemq.artemis.api.core.management.ManagementHelper
|
||||
import org.apache.activemq.artemis.reader.MessageUtil
|
||||
import rx.Notification
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
// The RPC protocol:
|
||||
@ -51,10 +60,6 @@ import java.util.*
|
||||
* Constants and data types used by the RPC API.
|
||||
*/
|
||||
object RPCApi {
|
||||
private val TAG_FIELD_NAME = "tag"
|
||||
private val RPC_ID_FIELD_NAME = "rpc-id"
|
||||
private val OBSERVABLE_ID_FIELD_NAME = "observable-id"
|
||||
private val METHOD_NAME_FIELD_NAME = "method-name"
|
||||
|
||||
/** Name of the Artemis queue on which the server receives RPC requests (as [ClientToServer.RpcRequest]). */
|
||||
const val RPC_SERVER_QUEUE_NAME = "rpc.server"
|
||||
@ -65,6 +70,7 @@ object RPCApi {
|
||||
const val RPC_CLIENT_QUEUE_NAME_PREFIX = "rpc.client"
|
||||
const val RPC_CLIENT_BINDING_REMOVALS = "rpc.clientqueueremovals"
|
||||
const val RPC_CLIENT_BINDING_ADDITIONS = "rpc.clientqueueadditions"
|
||||
const val RPC_TARGET_LEGAL_IDENTITY = "rpc-target-legal-identity"
|
||||
|
||||
val RPC_CLIENT_BINDING_REMOVAL_FILTER_EXPRESSION =
|
||||
"${ManagementHelper.HDR_NOTIFICATION_TYPE} = '${CoreNotificationType.BINDING_REMOVED.name}' AND " +
|
||||
@ -73,9 +79,6 @@ object RPCApi {
|
||||
"${ManagementHelper.HDR_NOTIFICATION_TYPE} = '${CoreNotificationType.BINDING_ADDED.name}' AND " +
|
||||
"${ManagementHelper.HDR_ROUTING_NAME} LIKE '$RPC_CLIENT_QUEUE_NAME_PREFIX.%'"
|
||||
|
||||
data class RpcRequestId(val toLong: Long)
|
||||
data class ObservableId(val toLong: Long)
|
||||
|
||||
object RpcRequestOrObservableIdKey
|
||||
|
||||
private fun ClientMessage.getBodyAsByteArray(): ByteArray {
|
||||
@ -101,28 +104,35 @@ object RPCApi {
|
||||
*/
|
||||
data class RpcRequest(
|
||||
val clientAddress: SimpleString,
|
||||
val id: RpcRequestId,
|
||||
val methodName: String,
|
||||
val serialisedArguments: ByteArray
|
||||
val serialisedArguments: ByteArray,
|
||||
val replyId: InvocationId,
|
||||
val sessionId: SessionId,
|
||||
val externalTrace: Trace? = null,
|
||||
val impersonatedActor: Actor? = null
|
||||
) : ClientToServer() {
|
||||
fun writeToClientMessage(message: ClientMessage) {
|
||||
MessageUtil.setJMSReplyTo(message, clientAddress)
|
||||
message.putIntProperty(TAG_FIELD_NAME, Tag.RPC_REQUEST.ordinal)
|
||||
message.putLongProperty(RPC_ID_FIELD_NAME, id.toLong)
|
||||
|
||||
replyId.mapTo(message)
|
||||
sessionId.mapTo(message)
|
||||
|
||||
externalTrace?.mapToExternal(message)
|
||||
impersonatedActor?.mapToImpersonated(message)
|
||||
|
||||
message.putStringProperty(METHOD_NAME_FIELD_NAME, methodName)
|
||||
message.bodyBuffer.writeBytes(serialisedArguments)
|
||||
}
|
||||
}
|
||||
|
||||
data class ObservablesClosed(
|
||||
val ids: List<ObservableId>
|
||||
) : ClientToServer() {
|
||||
data class ObservablesClosed(val ids: List<InvocationId>) : ClientToServer() {
|
||||
fun writeToClientMessage(message: ClientMessage) {
|
||||
message.putIntProperty(TAG_FIELD_NAME, Tag.OBSERVABLES_CLOSED.ordinal)
|
||||
val buffer = message.bodyBuffer
|
||||
buffer.writeInt(ids.size)
|
||||
ids.forEach {
|
||||
buffer.writeLong(it.toLong)
|
||||
buffer.writeInvocationId(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,16 +143,19 @@ object RPCApi {
|
||||
return when (tag) {
|
||||
RPCApi.ClientToServer.Tag.RPC_REQUEST -> RpcRequest(
|
||||
clientAddress = MessageUtil.getJMSReplyTo(message),
|
||||
id = RpcRequestId(message.getLongProperty(RPC_ID_FIELD_NAME)),
|
||||
methodName = message.getStringProperty(METHOD_NAME_FIELD_NAME),
|
||||
serialisedArguments = message.getBodyAsByteArray()
|
||||
serialisedArguments = message.getBodyAsByteArray(),
|
||||
replyId = message.replyId(),
|
||||
sessionId = message.sessionId(),
|
||||
externalTrace = message.externalTrace(),
|
||||
impersonatedActor = message.impersonatedActor()
|
||||
)
|
||||
RPCApi.ClientToServer.Tag.OBSERVABLES_CLOSED -> {
|
||||
val ids = ArrayList<ObservableId>()
|
||||
val ids = ArrayList<InvocationId>()
|
||||
val buffer = message.bodyBuffer
|
||||
val numberOfIds = buffer.readInt()
|
||||
for (i in 1..numberOfIds) {
|
||||
ids.add(ObservableId(buffer.readLong()))
|
||||
ids.add(buffer.readInvocationId())
|
||||
}
|
||||
ObservablesClosed(ids)
|
||||
}
|
||||
@ -164,23 +177,23 @@ object RPCApi {
|
||||
|
||||
/** Reply in response to an [ClientToServer.RpcRequest]. */
|
||||
data class RpcReply(
|
||||
val id: RpcRequestId,
|
||||
val id: InvocationId,
|
||||
val result: Try<Any?>
|
||||
) : ServerToClient() {
|
||||
override fun writeToClientMessage(context: SerializationContext, message: ClientMessage) {
|
||||
message.putIntProperty(TAG_FIELD_NAME, Tag.RPC_REPLY.ordinal)
|
||||
message.putLongProperty(RPC_ID_FIELD_NAME, id.toLong)
|
||||
id.mapTo(message, RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME)
|
||||
message.bodyBuffer.writeBytes(result.safeSerialize(context) { Try.Failure<Any>(it) }.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
data class Observation(
|
||||
val id: ObservableId,
|
||||
val id: InvocationId,
|
||||
val content: Notification<*>
|
||||
) : ServerToClient() {
|
||||
override fun writeToClientMessage(context: SerializationContext, message: ClientMessage) {
|
||||
message.putIntProperty(TAG_FIELD_NAME, Tag.OBSERVATION.ordinal)
|
||||
message.putLongProperty(OBSERVABLE_ID_FIELD_NAME, id.toLong)
|
||||
id.mapTo(message, OBSERVABLE_ID_FIELD_NAME, OBSERVABLE_ID_TIMESTAMP_FIELD_NAME)
|
||||
message.bodyBuffer.writeBytes(content.safeSerialize(context) { Notification.createOnError<Void?>(it) }.bytes)
|
||||
}
|
||||
}
|
||||
@ -196,20 +209,15 @@ object RPCApi {
|
||||
val tag = Tag.values()[message.getIntProperty(TAG_FIELD_NAME)]
|
||||
return when (tag) {
|
||||
RPCApi.ServerToClient.Tag.RPC_REPLY -> {
|
||||
val id = RpcRequestId(message.getLongProperty(RPC_ID_FIELD_NAME))
|
||||
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id.toLong)
|
||||
RpcReply(
|
||||
id = id,
|
||||
result = message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
|
||||
)
|
||||
val id = message.invocationId(RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot parse invocation id from client message.")
|
||||
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id)
|
||||
RpcReply(id, message.getBodyAsByteArray().deserialize(context = poolWithIdContext))
|
||||
}
|
||||
RPCApi.ServerToClient.Tag.OBSERVATION -> {
|
||||
val id = ObservableId(message.getLongProperty(OBSERVABLE_ID_FIELD_NAME))
|
||||
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, id.toLong)
|
||||
Observation(
|
||||
id = id,
|
||||
content = message.getBodyAsByteArray().deserialize(context = poolWithIdContext)
|
||||
)
|
||||
val observableId = message.invocationId(OBSERVABLE_ID_FIELD_NAME, OBSERVABLE_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot parse invocation id from client message.")
|
||||
val poolWithIdContext = context.withProperty(RpcRequestOrObservableIdKey, observableId)
|
||||
val payload = message.getBodyAsByteArray().deserialize<Notification<*>>(context = poolWithIdContext)
|
||||
Observation(observableId, payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,3 +236,108 @@ data class ArtemisConsumer(
|
||||
val session: ClientSession,
|
||||
val consumer: ClientConsumer
|
||||
)
|
||||
|
||||
private val TAG_FIELD_NAME = "tag"
|
||||
private val RPC_ID_FIELD_NAME = "rpc-id"
|
||||
private val RPC_ID_TIMESTAMP_FIELD_NAME = "rpc-id-timestamp"
|
||||
private val RPC_SESSION_ID_FIELD_NAME = "rpc-session-id"
|
||||
private val RPC_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-session-id-timestamp"
|
||||
private val RPC_EXTERNAL_ID_FIELD_NAME = "rpc-external-id"
|
||||
private val RPC_EXTERNAL_ID_TIMESTAMP_FIELD_NAME = "rpc-external-id-timestamp"
|
||||
private val RPC_EXTERNAL_SESSION_ID_FIELD_NAME = "rpc-external-session-id"
|
||||
private val RPC_EXTERNAL_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-external-session-id-timestamp"
|
||||
private val RPC_IMPERSONATED_ACTOR_ID = "rpc-impersonated-actor-id"
|
||||
private val RPC_IMPERSONATED_ACTOR_STORE_ID = "rpc-impersonated-actor-store-id"
|
||||
private val RPC_IMPERSONATED_ACTOR_OWNING_LEGAL_IDENTITY = "rpc-impersonated-actor-owningLegalIdentity"
|
||||
private val OBSERVABLE_ID_FIELD_NAME = "observable-id"
|
||||
private val OBSERVABLE_ID_TIMESTAMP_FIELD_NAME = "observable-id-timestamp"
|
||||
private val METHOD_NAME_FIELD_NAME = "method-name"
|
||||
|
||||
fun ClientMessage.replyId(): InvocationId {
|
||||
|
||||
return invocationId(RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract reply id from client message.")
|
||||
}
|
||||
|
||||
fun ClientMessage.sessionId(): SessionId {
|
||||
|
||||
return sessionId(RPC_SESSION_ID_FIELD_NAME, RPC_SESSION_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract the session id from client message.")
|
||||
}
|
||||
|
||||
fun ClientMessage.externalTrace(): Trace? {
|
||||
|
||||
val invocationId = invocationId(RPC_EXTERNAL_ID_FIELD_NAME, RPC_EXTERNAL_ID_TIMESTAMP_FIELD_NAME)
|
||||
val sessionId = sessionId(RPC_EXTERNAL_SESSION_ID_FIELD_NAME, RPC_EXTERNAL_SESSION_ID_TIMESTAMP_FIELD_NAME)
|
||||
|
||||
return when {
|
||||
invocationId == null || sessionId == null -> null
|
||||
else -> Trace(invocationId, sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun ClientMessage.impersonatedActor(): Actor? {
|
||||
|
||||
return getStringProperty(RPC_IMPERSONATED_ACTOR_ID)?.let {
|
||||
val impersonatedStoreId = getStringProperty(RPC_IMPERSONATED_ACTOR_STORE_ID)
|
||||
val impersonatingOwningLegalIdentity = getStringProperty(RPC_IMPERSONATED_ACTOR_OWNING_LEGAL_IDENTITY)
|
||||
if (impersonatedStoreId == null || impersonatingOwningLegalIdentity == null) {
|
||||
throw IllegalStateException("Cannot extract impersonated actor from client message.")
|
||||
}
|
||||
Actor(Actor.Id(it), AuthServiceId(impersonatedStoreId), CordaX500Name.parse(impersonatingOwningLegalIdentity))
|
||||
}
|
||||
}
|
||||
|
||||
private fun Id<String>.mapTo(message: ClientMessage, valueProperty: String, timestampProperty: String) {
|
||||
|
||||
message.putStringProperty(valueProperty, value)
|
||||
message.putLongProperty(timestampProperty, timestamp.toEpochMilli())
|
||||
}
|
||||
|
||||
private fun ActiveMQBuffer.writeInvocationId(invocationId: InvocationId) {
|
||||
|
||||
this.writeString(invocationId.value)
|
||||
this.writeLong(invocationId.timestamp.toEpochMilli())
|
||||
}
|
||||
|
||||
private fun ActiveMQBuffer.readInvocationId() : InvocationId {
|
||||
|
||||
val value = this.readString()
|
||||
val timestamp = this.readLong()
|
||||
return InvocationId(value, Instant.ofEpochMilli(timestamp))
|
||||
}
|
||||
|
||||
private fun InvocationId.mapTo(message: ClientMessage) = mapTo(message, RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME)
|
||||
|
||||
private fun SessionId.mapTo(message: ClientMessage) = mapTo(message, RPC_SESSION_ID_FIELD_NAME, RPC_SESSION_ID_TIMESTAMP_FIELD_NAME)
|
||||
|
||||
private fun Trace.mapToExternal(message: ClientMessage) = mapTo(message, RPC_EXTERNAL_ID_FIELD_NAME, RPC_EXTERNAL_ID_TIMESTAMP_FIELD_NAME, RPC_EXTERNAL_SESSION_ID_FIELD_NAME, RPC_EXTERNAL_SESSION_ID_TIMESTAMP_FIELD_NAME)
|
||||
|
||||
private fun Actor.mapToImpersonated(message: ClientMessage) {
|
||||
|
||||
message.putStringProperty(RPC_IMPERSONATED_ACTOR_ID, this.id.value)
|
||||
message.putStringProperty(RPC_IMPERSONATED_ACTOR_STORE_ID, this.serviceId.value)
|
||||
message.putStringProperty(RPC_IMPERSONATED_ACTOR_OWNING_LEGAL_IDENTITY, this.owningLegalIdentity.toString())
|
||||
}
|
||||
|
||||
private fun Trace.mapTo(message: ClientMessage, valueProperty: String, timestampProperty: String, sessionValueProperty: String, sessionTimestampProperty: String) = apply {
|
||||
|
||||
invocationId.apply {
|
||||
message.putStringProperty(valueProperty, value)
|
||||
message.putLongProperty(timestampProperty, timestamp.toEpochMilli())
|
||||
}
|
||||
sessionId.apply {
|
||||
message.putStringProperty(sessionValueProperty, value)
|
||||
message.putLongProperty(sessionTimestampProperty, timestamp.toEpochMilli())
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClientMessage.invocationId(valueProperty: String, timestampProperty: String): InvocationId? = id(valueProperty, timestampProperty, ::InvocationId)
|
||||
|
||||
private fun ClientMessage.sessionId(valueProperty: String, timestampProperty: String): SessionId? = id(valueProperty, timestampProperty, ::SessionId)
|
||||
|
||||
private fun <ID : Id<*>> ClientMessage.id(valueProperty: String, timestampProperty: String, construct: (value: String, timestamp: Instant) -> ID): ID? {
|
||||
|
||||
// returning null because getLongProperty throws trying to convert null to long
|
||||
val idRaw = this.getStringProperty(valueProperty) ?: return null
|
||||
val timestampRaw = this.getLongProperty(timestampProperty)
|
||||
return construct(idRaw, Instant.ofEpochMilli(timestampRaw))
|
||||
}
|
@ -34,6 +34,7 @@ import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
|
@ -18,6 +18,7 @@ import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.startFlow
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import org.junit.Test
|
||||
|
@ -11,6 +11,7 @@ import net.corda.testing.ALICE
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -24,10 +24,7 @@ import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEERS_PREFIX
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.configureTestSSL
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import net.corda.testing.messaging.SimpleMQClient
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
|
||||
|
@ -7,6 +7,7 @@ import net.corda.confidential.SwapIdentitiesFlow
|
||||
import net.corda.confidential.SwapIdentitiesHandler
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.flows.*
|
||||
@ -347,8 +348,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
private fun <T> startFlowChecked(flow: FlowLogic<T>): FlowStateMachine<T> {
|
||||
val logicType = flow.javaClass
|
||||
require(logicType.isAnnotationPresent(StartableByService::class.java)) { "${logicType.name} was not designed for starting by a CordaService" }
|
||||
val currentUser = FlowInitiator.Service(serviceInstance.javaClass.name)
|
||||
return flowStarter.startFlow(flow, currentUser).getOrThrow()
|
||||
// TODO check service permissions
|
||||
// TODO switch from myInfo.legalIdentities[0].name to current node's identity as soon as available
|
||||
val context = InvocationContext.service(serviceInstance.javaClass.name, myInfo.legalIdentities[0].name)
|
||||
return flowStarter.startFlow(flow, context).getOrThrow()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@ -766,8 +769,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
internal class FlowStarterImpl(private val serverThread: AffinityExecutor, private val smm: StateMachineManager) : FlowStarter {
|
||||
override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator, ourIdentity: Party?): CordaFuture<FlowStateMachine<T>> {
|
||||
return serverThread.fetchFrom { smm.startFlow(logic, flowInitiator, ourIdentity) }
|
||||
override fun <T> startFlow(logic: FlowLogic<T>, context: InvocationContext): CordaFuture<FlowStateMachine<T>> {
|
||||
return serverThread.fetchFrom { smm.startFlow(logic, context) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ package net.corda.node.internal
|
||||
|
||||
import net.corda.client.rpc.notUsed
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
@ -22,7 +24,7 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.api.FlowStarter
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.messaging.rpcContext
|
||||
import net.corda.node.services.messaging.context
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
import net.corda.node.utilities.CordaPersistence
|
||||
import rx.Observable
|
||||
@ -151,9 +153,7 @@ internal class CordaRPCOpsImpl(
|
||||
|
||||
private fun <T> startFlow(logicType: Class<out FlowLogic<T>>, args: Array<out Any?>): FlowStateMachine<T> {
|
||||
require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" }
|
||||
val currentUser = FlowInitiator.RPC(rpcContext().currentUser.username)
|
||||
// TODO RPC flows should have mapping user -> identity that should be resolved automatically on starting flow.
|
||||
return flowStarter.invokeFlowAsync(logicType, currentUser, *args).getOrThrow()
|
||||
return flowStarter.invokeFlowAsync(logicType, context(), *args).getOrThrow()
|
||||
}
|
||||
|
||||
override fun attachmentExists(id: SecureHash): Boolean {
|
||||
@ -192,7 +192,7 @@ internal class CordaRPCOpsImpl(
|
||||
} catch (e: Exception) {
|
||||
// log and rethrow exception so we keep a copy server side
|
||||
log.error(e.message)
|
||||
throw e.cause ?: e
|
||||
throw e.cause ?: e
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,17 +272,30 @@ internal class CordaRPCOpsImpl(
|
||||
return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun stateMachineInfoFromFlowLogic(flowLogic: FlowLogic<*>): StateMachineInfo {
|
||||
return StateMachineInfo(flowLogic.runId, flowLogic.javaClass.name, flowLogic.stateMachine.flowInitiator, flowLogic.track())
|
||||
}
|
||||
private fun stateMachineInfoFromFlowLogic(flowLogic: FlowLogic<*>): StateMachineInfo {
|
||||
return StateMachineInfo(flowLogic.runId, flowLogic.javaClass.name, flowLogic.stateMachine.context.toFlowInitiator(), flowLogic.track(), flowLogic.stateMachine.context)
|
||||
}
|
||||
|
||||
private fun stateMachineUpdateFromStateMachineChange(change: StateMachineManager.Change): StateMachineUpdate {
|
||||
return when (change) {
|
||||
is StateMachineManager.Change.Add -> StateMachineUpdate.Added(stateMachineInfoFromFlowLogic(change.logic))
|
||||
is StateMachineManager.Change.Removed -> StateMachineUpdate.Removed(change.logic.runId, change.result)
|
||||
}
|
||||
private fun stateMachineUpdateFromStateMachineChange(change: StateMachineManager.Change): StateMachineUpdate {
|
||||
return when (change) {
|
||||
is StateMachineManager.Change.Add -> StateMachineUpdate.Added(stateMachineInfoFromFlowLogic(change.logic))
|
||||
is StateMachineManager.Change.Removed -> StateMachineUpdate.Removed(change.logic.runId, change.result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun InvocationContext.toFlowInitiator(): FlowInitiator {
|
||||
|
||||
val principal = origin.principal().name
|
||||
return when (origin) {
|
||||
is Origin.RPC -> FlowInitiator.RPC(principal)
|
||||
is Origin.Peer -> services.identityService.wellKnownPartyFromX500Name((origin as Origin.Peer).party)?.let { FlowInitiator.Peer(it) } ?: throw IllegalStateException("Unknown peer with name ${(origin as Origin.Peer).party}.")
|
||||
is Origin.Service -> FlowInitiator.Service(principal)
|
||||
is Origin.Shell -> FlowInitiator.Shell
|
||||
is Origin.Scheduled -> FlowInitiator.Scheduled((origin as Origin.Scheduled).scheduledState)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = loggerFor<CordaRPCOpsImpl>()
|
||||
}
|
||||
}
|
@ -14,14 +14,13 @@ import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.vault.*
|
||||
import net.corda.node.services.messaging.RpcContext
|
||||
import net.corda.node.services.messaging.requireEitherPermission
|
||||
import net.corda.node.services.messaging.RpcAuthContext
|
||||
import rx.Observable
|
||||
import java.io.InputStream
|
||||
import java.security.PublicKey
|
||||
|
||||
// TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140
|
||||
class RpcAuthorisationProxy(private val implementation: CordaRPCOps, private val context: () -> RpcContext, private val permissionsAllowing: (methodName: String, args: List<Any?>) -> Set<String>) : CordaRPCOps {
|
||||
class RpcAuthorisationProxy(private val implementation: CordaRPCOps, private val context: () -> RpcAuthContext, private val permissionsAllowing: (methodName: String, args: List<Any?>) -> Set<String>) : CordaRPCOps {
|
||||
|
||||
override fun uploadAttachmentWithMetadata(jar: InputStream, uploader: String, filename: String): SecureHash = guard("uploadAttachmentWithMetadata") {
|
||||
implementation.uploadAttachmentWithMetadata(jar, uploader, filename)
|
||||
@ -163,7 +162,7 @@ class RpcAuthorisationProxy(private val implementation: CordaRPCOps, private val
|
||||
// TODO change to KFunction reference after Kotlin fixes https://youtrack.jetbrains.com/issue/KT-12140
|
||||
private inline fun <RESULT> guard(methodName: String, args: List<Any?>, action: () -> RESULT): RESULT {
|
||||
|
||||
context.invoke().requireEitherPermission(permissionsAllowing.invoke(methodName, args))
|
||||
return action.invoke()
|
||||
context().requireEitherPermission(permissionsAllowing.invoke(methodName, args))
|
||||
return action()
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.nodeapi.User
|
||||
|
||||
/**
|
||||
@ -11,12 +12,17 @@ interface RPCUserService {
|
||||
|
||||
fun getUser(username: String): User?
|
||||
val users: List<User>
|
||||
|
||||
val id: AuthServiceId
|
||||
}
|
||||
|
||||
// TODO Store passwords as salted hashes
|
||||
// TODO Or ditch this and consider something like Apache Shiro
|
||||
// TODO Need access to permission checks from inside flows and at other point during audit checking.
|
||||
class RPCUserServiceImpl(override val users: List<User>) : RPCUserService {
|
||||
|
||||
override val id: AuthServiceId = AuthServiceId("NODE_FILE_CONFIGURATION")
|
||||
|
||||
init {
|
||||
users.forEach {
|
||||
require(it.username.matches("\\w+".toRegex())) { "Username ${it.username} contains invalid characters" }
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.corda.node.services.api
|
||||
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import java.security.Principal
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
@ -17,9 +17,9 @@ sealed class AuditEvent {
|
||||
*/
|
||||
abstract val timestamp: Instant
|
||||
/**
|
||||
* The responsible individual, node, or subsystem to which the audit event can be mapped.
|
||||
* The invocation context at the time the event was generated.
|
||||
*/
|
||||
abstract val principal: Principal
|
||||
abstract val context: InvocationContext
|
||||
/**
|
||||
* A human readable description of audit event including any permission check results.
|
||||
*/
|
||||
@ -36,7 +36,7 @@ sealed class AuditEvent {
|
||||
* Sealed data class to mark system related events as a distinct category.
|
||||
*/
|
||||
data class SystemAuditEvent(override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val contextData: Map<String, String>) : AuditEvent()
|
||||
|
||||
@ -60,7 +60,7 @@ interface FlowAuditInfo {
|
||||
*/
|
||||
data class FlowAppAuditEvent(
|
||||
override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val contextData: Map<String, String>,
|
||||
override val flowType: Class<out FlowLogic<*>>,
|
||||
@ -73,7 +73,7 @@ data class FlowAppAuditEvent(
|
||||
*/
|
||||
data class FlowStartEvent(
|
||||
override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val contextData: Map<String, String>,
|
||||
override val flowType: Class<out FlowLogic<*>>,
|
||||
@ -86,7 +86,7 @@ data class FlowStartEvent(
|
||||
*/
|
||||
data class FlowProgressAuditEvent(
|
||||
override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val flowType: Class<out FlowLogic<*>>,
|
||||
override val flowId: StateMachineRunId,
|
||||
@ -98,7 +98,7 @@ data class FlowProgressAuditEvent(
|
||||
* Sealed data class to record any FlowExceptions, or other unexpected terminations of a Flow.
|
||||
*/
|
||||
data class FlowErrorAuditEvent(override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val contextData: Map<String, String>,
|
||||
override val flowType: Class<out FlowLogic<*>>,
|
||||
@ -111,7 +111,7 @@ data class FlowErrorAuditEvent(override val timestamp: Instant,
|
||||
* after recording the FlowPermissionAuditEvent. This may cause an extra FlowErrorAuditEvent to be recorded too.
|
||||
*/
|
||||
data class FlowPermissionAuditEvent(override val timestamp: Instant,
|
||||
override val principal: Principal,
|
||||
override val context: InvocationContext,
|
||||
override val description: String,
|
||||
override val contextData: Map<String, String>,
|
||||
override val flowType: Class<out FlowLogic<*>>,
|
||||
|
@ -2,12 +2,10 @@ package net.corda.node.services.api
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
@ -18,7 +16,6 @@ import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.NetworkMapCacheBase
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.internal.cordapp.CordappProviderInternal
|
||||
@ -122,34 +119,28 @@ interface ServiceHubInternal : ServiceHub {
|
||||
}
|
||||
|
||||
interface FlowStarter {
|
||||
/**
|
||||
* Starts an already constructed flow. Note that you must be on the server thread to call this method. [FlowInitiator]
|
||||
* defaults to [FlowInitiator.RPC] with username "Only For Testing".
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun <T> startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = startFlow(logic, FlowInitiator.RPC("Only For Testing")).getOrThrow()
|
||||
|
||||
/**
|
||||
* Starts an already constructed flow. Note that you must be on the server thread to call this method.
|
||||
* @param flowInitiator indicates who started the flow, see: [FlowInitiator].
|
||||
* @param context indicates who started the flow, see: [InvocationContext].
|
||||
*/
|
||||
fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator, ourIdentity: Party? = null): CordaFuture<FlowStateMachine<T>>
|
||||
fun <T> startFlow(logic: FlowLogic<T>, context: InvocationContext): CordaFuture<FlowStateMachine<T>>
|
||||
|
||||
/**
|
||||
* Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow.
|
||||
* Note that you must be on the server thread to call this method. [flowInitiator] points how flow was started,
|
||||
* See: [FlowInitiator].
|
||||
* Note that you must be on the server thread to call this method. [context] points how flow was started,
|
||||
* See: [InvocationContext].
|
||||
*
|
||||
* @throws net.corda.core.flows.IllegalFlowLogicException or IllegalArgumentException if there are problems with the
|
||||
* [logicType] or [args].
|
||||
*/
|
||||
fun <T> invokeFlowAsync(
|
||||
logicType: Class<out FlowLogic<T>>,
|
||||
flowInitiator: FlowInitiator,
|
||||
context: InvocationContext,
|
||||
vararg args: Any?): CordaFuture<FlowStateMachine<T>> {
|
||||
val logicRef = FlowLogicRefFactoryImpl.createForRPC(logicType, *args)
|
||||
val logic: FlowLogic<T> = uncheckedCast(FlowLogicRefFactoryImpl.toFlowLogic(logicRef))
|
||||
return startFlow(logic, flowInitiator, ourIdentity = null)
|
||||
return startFlow(logic, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,17 +2,17 @@ package net.corda.node.services.events
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.common.util.concurrent.SettableFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.contracts.SchedulableState
|
||||
import net.corda.core.contracts.ScheduledActivity
|
||||
import net.corda.core.contracts.ScheduledStateRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.internal.until
|
||||
import net.corda.core.node.StateLoader
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
@ -247,7 +247,9 @@ class NodeSchedulerService(private val clock: Clock,
|
||||
val scheduledFlow = getScheduledFlow(scheduledState)
|
||||
if (scheduledFlow != null) {
|
||||
flowName = scheduledFlow.javaClass.name
|
||||
val future = flowStarter.startFlow(scheduledFlow, FlowInitiator.Scheduled(scheduledState)).flatMap { it.resultFuture }
|
||||
// TODO refactor the scheduler to store and propagate the original invocation context
|
||||
val context = InvocationContext.newInstance(Origin.Scheduled(scheduledState))
|
||||
val future = flowStarter.startFlow(scheduledFlow, context).flatMap { it.resultFuture }
|
||||
future.then {
|
||||
unfinishedSchedules.countDown()
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package net.corda.node.services.logging
|
||||
|
||||
import net.corda.core.context.InvocationContext
|
||||
import org.slf4j.MDC
|
||||
|
||||
internal fun InvocationContext.pushToLoggingContext() {
|
||||
|
||||
MDC.put("invocation_id", trace.invocationId.value)
|
||||
MDC.put("invocation_timestamp", trace.invocationId.timestamp.toString())
|
||||
MDC.put("session_id", trace.sessionId.value)
|
||||
MDC.put("session_timestamp", trace.sessionId.timestamp.toString())
|
||||
actor?.let {
|
||||
MDC.put("actor_id", it.id.value)
|
||||
MDC.put("actor_store_id", it.serviceId.value)
|
||||
MDC.put("actor_owningIdentity", it.owningLegalIdentity.toString())
|
||||
}
|
||||
externalTrace?.let {
|
||||
MDC.put("external_invocation_id", it.invocationId.value)
|
||||
MDC.put("external_invocation_timestamp", it.invocationId.timestamp.toString())
|
||||
MDC.put("external_session_id", it.sessionId.value)
|
||||
MDC.put("external_session_timestamp", it.sessionId.timestamp.toString())
|
||||
}
|
||||
impersonatedActor?.let {
|
||||
MDC.put("impersonating_actor_id", it.id.value)
|
||||
MDC.put("impersonating_actor_store_id", it.serviceId.value)
|
||||
MDC.put("impersonating_actor_owningIdentity", it.owningLegalIdentity.toString())
|
||||
}
|
||||
}
|
@ -12,7 +12,11 @@ import com.google.common.collect.Multimaps
|
||||
import com.google.common.collect.SetMultimap
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.Actor.Id
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.context.Trace.InvocationId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.LazyStickyPool
|
||||
import net.corda.core.internal.LifeCycle
|
||||
@ -25,6 +29,7 @@ import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.RPCUserService
|
||||
import net.corda.node.services.logging.pushToLoggingContext
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import org.apache.activemq.artemis.api.core.Message
|
||||
@ -37,6 +42,7 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator
|
||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||
import org.apache.activemq.artemis.api.core.management.CoreNotificationType
|
||||
import org.apache.activemq.artemis.api.core.management.ManagementHelper
|
||||
import org.slf4j.MDC
|
||||
import rx.Notification
|
||||
import rx.Observable
|
||||
import rx.Subscriber
|
||||
@ -106,7 +112,7 @@ class RPCServer(
|
||||
/** The observable subscription mapping. */
|
||||
private val observableMap = createObservableSubscriptionMap()
|
||||
/** A mapping from client addresses to IDs of associated Observables */
|
||||
private val clientAddressToObservables = Multimaps.synchronizedSetMultimap(HashMultimap.create<SimpleString, RPCApi.ObservableId>())
|
||||
private val clientAddressToObservables = Multimaps.synchronizedSetMultimap(HashMultimap.create<SimpleString, InvocationId>())
|
||||
/** The scheduled reaper handle. */
|
||||
private var reaperScheduledFuture: ScheduledFuture<*>? = null
|
||||
|
||||
@ -138,7 +144,7 @@ class RPCServer(
|
||||
}
|
||||
|
||||
private fun createObservableSubscriptionMap(): ObservableSubscriptionMap {
|
||||
val onObservableRemove = RemovalListener<RPCApi.ObservableId, ObservableSubscription> {
|
||||
val onObservableRemove = RemovalListener<InvocationId, ObservableSubscription> {
|
||||
log.debug { "Unsubscribing from Observable with id ${it.key} because of ${it.cause}" }
|
||||
it.value.subscription.unsubscribe()
|
||||
}
|
||||
@ -269,18 +275,19 @@ class RPCServer(
|
||||
val arguments = Try.on {
|
||||
clientToServer.serialisedArguments.deserialize<List<Any?>>(context = RPC_SERVER_CONTEXT)
|
||||
}
|
||||
val context = artemisMessage.context(clientToServer.sessionId)
|
||||
context.invocation.pushToLoggingContext()
|
||||
when (arguments) {
|
||||
is Try.Success -> {
|
||||
val rpcContext = RpcContext(currentUser = getUser(artemisMessage))
|
||||
rpcExecutor!!.submit {
|
||||
val result = invokeRpc(rpcContext, clientToServer.methodName, arguments.value)
|
||||
sendReply(clientToServer.id, clientToServer.clientAddress, result)
|
||||
val result = invokeRpc(context, clientToServer.methodName, arguments.value)
|
||||
sendReply(clientToServer.replyId, clientToServer.clientAddress, result)
|
||||
}
|
||||
}
|
||||
is Try.Failure -> {
|
||||
// We failed to deserialise the arguments, route back the error
|
||||
log.warn("Inbound RPC failed", arguments.exception)
|
||||
sendReply(clientToServer.id, clientToServer.clientAddress, arguments)
|
||||
sendReply(clientToServer.replyId, clientToServer.clientAddress, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,10 +298,10 @@ class RPCServer(
|
||||
artemisMessage.acknowledge()
|
||||
}
|
||||
|
||||
private fun invokeRpc(rpcContext: RpcContext, methodName: String, arguments: List<Any?>): Try<Any> {
|
||||
private fun invokeRpc(context: RpcAuthContext, methodName: String, arguments: List<Any?>): Try<Any> {
|
||||
return Try.on {
|
||||
try {
|
||||
CURRENT_RPC_CONTEXT.set(rpcContext)
|
||||
CURRENT_RPC_CONTEXT.set(context)
|
||||
log.debug { "Calling $methodName" }
|
||||
val method = methodTable[methodName] ?:
|
||||
throw RPCException("Received RPC for unknown method $methodName - possible client/server version skew?")
|
||||
@ -307,10 +314,10 @@ class RPCServer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendReply(requestId: RPCApi.RpcRequestId, clientAddress: SimpleString, result: Try<Any>) {
|
||||
val reply = RPCApi.ServerToClient.RpcReply(requestId, result)
|
||||
private fun sendReply(replyId: InvocationId, clientAddress: SimpleString, result: Try<Any>) {
|
||||
val reply = RPCApi.ServerToClient.RpcReply(replyId, result)
|
||||
val observableContext = ObservableContext(
|
||||
requestId,
|
||||
replyId,
|
||||
observableMap,
|
||||
clientAddressToObservables,
|
||||
clientAddress,
|
||||
@ -352,51 +359,83 @@ class RPCServer(
|
||||
// TODO remove this User once webserver doesn't need it
|
||||
private val nodeUser = User(NODE_USER, NODE_USER, setOf())
|
||||
|
||||
private fun getUser(message: ClientMessage): User {
|
||||
private fun ClientMessage.context(sessionId: Trace.SessionId): RpcAuthContext {
|
||||
val trace = Trace.newInstance(sessionId = sessionId)
|
||||
val externalTrace = externalTrace()
|
||||
val rpcActor = actorFrom(this)
|
||||
val impersonatedActor = impersonatedActor()
|
||||
return RpcAuthContext(InvocationContext.rpc(rpcActor.first, trace, externalTrace, impersonatedActor), rpcActor.second)
|
||||
}
|
||||
|
||||
private fun actorFrom(message: ClientMessage): Pair<Actor, RpcPermissions> {
|
||||
val validatedUser = message.getStringProperty(Message.HDR_VALIDATED_USER) ?: throw IllegalArgumentException("Missing validated user from the Artemis message")
|
||||
val targetLegalIdentity = message.getStringProperty(RPCApi.RPC_TARGET_LEGAL_IDENTITY)?.let(CordaX500Name.Companion::parse) ?: nodeLegalName
|
||||
// TODO switch userService based on targetLegalIdentity
|
||||
val rpcUser = userService.getUser(validatedUser)
|
||||
if (rpcUser != null) {
|
||||
return rpcUser
|
||||
return if (rpcUser != null) {
|
||||
Actor(Id(rpcUser.username), userService.id, targetLegalIdentity) to RpcPermissions(rpcUser.permissions)
|
||||
} else if (CordaX500Name.parse(validatedUser) == nodeLegalName) {
|
||||
return nodeUser
|
||||
// TODO remove this after Shell and WebServer will no longer need it
|
||||
Actor(Id(nodeUser.username), userService.id, targetLegalIdentity) to RpcPermissions(nodeUser.permissions)
|
||||
} else {
|
||||
throw IllegalArgumentException("Validated user '$validatedUser' is not an RPC user nor the NODE user")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace this by creating a new CordaRPCImpl for each request, passing the context, after we fix Shell and WebServer
|
||||
@JvmField
|
||||
internal val CURRENT_RPC_CONTEXT: ThreadLocal<RpcContext> = ThreadLocal()
|
||||
internal val CURRENT_RPC_CONTEXT: ThreadLocal<RpcAuthContext> = CurrentRpcContext()
|
||||
|
||||
internal class CurrentRpcContext : ThreadLocal<RpcAuthContext>() {
|
||||
|
||||
override fun remove() {
|
||||
super.remove()
|
||||
MDC.clear()
|
||||
}
|
||||
|
||||
override fun set(context: RpcAuthContext?) {
|
||||
when {
|
||||
context != null -> {
|
||||
super.set(context)
|
||||
// this is needed here as well because the Shell sets the context without going through the RpcServer
|
||||
context.invocation.pushToLoggingContext()
|
||||
}
|
||||
else -> remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a context specific to the current RPC call. Note that trying to call this function outside of an RPC will
|
||||
* throw. If you'd like to use the context outside of the call (e.g. in another thread) then pass the returned reference
|
||||
* around explicitly.
|
||||
* The [InvocationContext] does not include permissions.
|
||||
*/
|
||||
fun rpcContext(): RpcContext = CURRENT_RPC_CONTEXT.get()
|
||||
internal fun context(): InvocationContext = rpcContext().invocation
|
||||
|
||||
/**
|
||||
* @param currentUser This is available to RPC implementations to query the validated [User] that is calling it. Each
|
||||
* user has a set of permissions they're entitled to which can be used to control access.
|
||||
* Returns a context specific to the current RPC call. Note that trying to call this function outside of an RPC will
|
||||
* throw. If you'd like to use the context outside of the call (e.g. in another thread) then pass the returned reference
|
||||
* around explicitly.
|
||||
* The [RpcAuthContext] includes permissions.
|
||||
*/
|
||||
data class RpcContext(
|
||||
val currentUser: User
|
||||
)
|
||||
fun rpcContext(): RpcAuthContext = CURRENT_RPC_CONTEXT.get()
|
||||
|
||||
class ObservableSubscription(
|
||||
val subscription: Subscription
|
||||
)
|
||||
|
||||
typealias ObservableSubscriptionMap = Cache<RPCApi.ObservableId, ObservableSubscription>
|
||||
typealias ObservableSubscriptionMap = Cache<InvocationId, ObservableSubscription>
|
||||
|
||||
// We construct an observable context on each RPC request. If subsequently a nested Observable is
|
||||
// encountered this same context is propagated by the instrumented KryoPool. This way all
|
||||
// observations rooted in a single RPC will be muxed correctly. Note that the context construction
|
||||
// itself is quite cheap.
|
||||
class ObservableContext(
|
||||
val rpcRequestId: RPCApi.RpcRequestId,
|
||||
val invocationId: InvocationId,
|
||||
val observableMap: ObservableSubscriptionMap,
|
||||
val clientAddressToObservables: SetMultimap<SimpleString, RPCApi.ObservableId>,
|
||||
val clientAddressToObservables: SetMultimap<SimpleString, InvocationId>,
|
||||
val clientAddress: SimpleString,
|
||||
val serverControl: ActiveMQServerControl,
|
||||
val sessionAndProducerPool: LazyStickyPool<ArtemisProducer>,
|
||||
@ -410,7 +449,7 @@ class ObservableContext(
|
||||
|
||||
fun sendMessage(serverToClient: RPCApi.ServerToClient) {
|
||||
try {
|
||||
sessionAndProducerPool.run(rpcRequestId) {
|
||||
sessionAndProducerPool.run(invocationId) {
|
||||
val artemisMessage = it.session.createMessage(false)
|
||||
serverToClient.writeToClientMessage(serializationContextWithObservableContext, artemisMessage)
|
||||
it.producer.send(clientAddress, artemisMessage)
|
||||
@ -437,9 +476,9 @@ object RpcServerObservableSerializer : Serializer<Observable<*>>() {
|
||||
}
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, observable: Observable<*>) {
|
||||
val observableId = RPCApi.ObservableId(random63BitValue())
|
||||
val observableId = InvocationId.newInstance()
|
||||
val observableContext = kryo.context[RpcObservableContextKey] as ObservableContext
|
||||
output.writeLong(observableId.toLong, true)
|
||||
output.writeInvocationId(observableId)
|
||||
val observableWithSubscription = ObservableSubscription(
|
||||
// We capture [observableContext] in the subscriber. Note that all synchronisation/kryo borrowing
|
||||
// must be done again within the subscriber
|
||||
@ -465,4 +504,10 @@ object RpcServerObservableSerializer : Serializer<Observable<*>>() {
|
||||
observableContext.clientAddressToObservables.put(observableContext.clientAddress, observableId)
|
||||
observableContext.observableMap.put(observableId, observableWithSubscription)
|
||||
}
|
||||
|
||||
private fun Output.writeInvocationId(id: InvocationId) {
|
||||
|
||||
writeString(id.value)
|
||||
writeLong(id.timestamp.toEpochMilli())
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
@file:JvmName("RPCServerStructures")
|
||||
|
||||
package net.corda.node.services.messaging
|
||||
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
|
||||
/** Helper method which checks that the current RPC user is entitled for the given permission. Throws a [PermissionException] otherwise. */
|
||||
fun RpcContext.requirePermission(permission: String): RpcContext = requireEitherPermission(setOf(permission))
|
||||
|
||||
/** Helper method which checks that the current RPC user is entitled with any of the given permissions. Throws a [PermissionException] otherwise. */
|
||||
fun RpcContext.requireEitherPermission(permissions: Set<String>): RpcContext {
|
||||
// TODO remove the NODE_USER condition once webserver doesn't need it
|
||||
val currentUserPermissions = currentUser.permissions
|
||||
if (currentUser.username != ArtemisMessagingComponent.NODE_USER && currentUserPermissions.intersect(permissions + all()).isEmpty()) {
|
||||
throw PermissionException("User not permissioned with any of $permissions, permissions are $currentUserPermissions")
|
||||
}
|
||||
return this
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package net.corda.node.services.messaging
|
||||
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.node.services.Permissions
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
|
||||
data class RpcAuthContext(val invocation: InvocationContext, val grantedPermissions: RpcPermissions) {
|
||||
|
||||
fun requirePermission(permission: String) = requireEitherPermission(setOf(permission))
|
||||
|
||||
fun requireEitherPermission(permissions: Set<String>): RpcAuthContext {
|
||||
|
||||
// TODO remove the NODE_USER condition once webserver and shell won't need it anymore
|
||||
if (invocation.principal().name != ArtemisMessagingComponent.NODE_USER && !grantedPermissions.coverAny(permissions)) {
|
||||
throw PermissionException("User not permissioned with any of $permissions, permissions are ${this.grantedPermissions}.")
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
data class RpcPermissions(private val values: Set<String> = emptySet()) {
|
||||
|
||||
companion object {
|
||||
val NONE = RpcPermissions()
|
||||
}
|
||||
|
||||
fun coverAny(permissions: Set<String>) = !values.intersect(permissions + Permissions.all()).isEmpty()
|
||||
}
|
@ -15,6 +15,7 @@ import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.OpenFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -22,6 +23,7 @@ import net.corda.core.utilities.*
|
||||
import net.corda.node.services.api.FlowAppAuditEvent
|
||||
import net.corda.node.services.api.FlowPermissionAuditEvent
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.logging.pushToLoggingContext
|
||||
import net.corda.node.services.statemachine.FlowSessionState.Initiating
|
||||
import net.corda.node.utilities.CordaPersistence
|
||||
import net.corda.node.utilities.DatabaseTransaction
|
||||
@ -40,9 +42,8 @@ class FlowPermissionException(message: String) : FlowException(message)
|
||||
class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
override val logic: FlowLogic<R>,
|
||||
scheduler: FiberScheduler,
|
||||
override val flowInitiator: FlowInitiator,
|
||||
// Store the Party rather than the full cert path with PartyAndCertificate
|
||||
val ourIdentity: Party) : Fiber<Unit>(id.toString(), scheduler), FlowStateMachine<R> {
|
||||
val ourIdentity: Party,
|
||||
override val context: InvocationContext) : Fiber<Unit>(id.toString(), scheduler), FlowStateMachine<R> {
|
||||
|
||||
companion object {
|
||||
// Used to work around a small limitation in Quasar.
|
||||
@ -254,7 +255,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
val permissionGranted = true // TODO define permission control service on ServiceHubInternal and actually check authorization.
|
||||
val checkPermissionEvent = FlowPermissionAuditEvent(
|
||||
serviceHub.clock.instant(),
|
||||
flowInitiator,
|
||||
context,
|
||||
"Flow Permission Required: $permissionName",
|
||||
extraAuditData,
|
||||
logic.javaClass,
|
||||
@ -264,7 +265,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
serviceHub.auditService.recordAuditEvent(checkPermissionEvent)
|
||||
@Suppress("ConstantConditionIf")
|
||||
if (!permissionGranted) {
|
||||
throw FlowPermissionException("User $flowInitiator not permissioned for $permissionName on flow $id")
|
||||
throw FlowPermissionException("User ${context.principal()} not permissioned for $permissionName on flow $id")
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +273,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
override fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String, String>) {
|
||||
val flowAuditEvent = FlowAppAuditEvent(
|
||||
serviceHub.clock.instant(),
|
||||
flowInitiator,
|
||||
context,
|
||||
comment,
|
||||
extraAuditData,
|
||||
logic.javaClass,
|
||||
@ -306,6 +307,8 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun pushToLoggingContext() = context.pushToLoggingContext()
|
||||
|
||||
/**
|
||||
* This method will suspend the state machine and wait for incoming session init response from other party.
|
||||
*/
|
||||
@ -392,6 +395,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
session.retryable = retryable
|
||||
val (version, initiatingFlowClass) = session.flow.javaClass.flowVersionAndInitiatingClass
|
||||
val payloadBytes = firstPayload?.serialize(context = SerializationDefaults.P2P_CONTEXT)
|
||||
logger.info("Initiating flow session with party ${otherParty.name}. Session id for tracing purposes is ${session.ourSessionId}.")
|
||||
val sessionInit = SessionInit(session.ourSessionId, initiatingFlowClass.name, version, session.flow.javaClass.appName, payloadBytes)
|
||||
sendInternal(session, sessionInit)
|
||||
if (waitForConfirmation) {
|
||||
|
@ -1,10 +1,9 @@
|
||||
package net.corda.node.services.statemachine
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.utilities.Try
|
||||
import rx.Observable
|
||||
@ -20,8 +19,7 @@ import rx.Observable
|
||||
* A flow is a class with a single call method. The call method and any others it invokes are rewritten by a bytecode
|
||||
* rewriting engine called Quasar, to ensure the code can be suspended and resumed at any point.
|
||||
*
|
||||
* TODO: Consider the issue of continuation identity more deeply: is it a safe assumption that a serialised
|
||||
* continuation is always unique?
|
||||
* TODO: Consider the issue of continuation identity more deeply: is it a safe assumption that a serialised continuation is always unique?
|
||||
* TODO: Think about how to bring the system to a clean stop so it can be upgraded without any serialised stacks on disk
|
||||
* TODO: Timeouts
|
||||
* TODO: Surfacing of exceptions via an API and/or management UI
|
||||
@ -43,9 +41,9 @@ interface StateMachineManager {
|
||||
* Starts a new flow.
|
||||
*
|
||||
* @param flowLogic The flow's code.
|
||||
* @param flowInitiator The initiator of the flow.
|
||||
* @param context The context of the flow.
|
||||
*/
|
||||
fun <A> startFlow(flowLogic: FlowLogic<A>, flowInitiator: FlowInitiator, ourIdentity: Party? = null): CordaFuture<FlowStateMachine<A>>
|
||||
fun <A> startFlow(flowLogic: FlowLogic<A>, context: InvocationContext): CordaFuture<FlowStateMachine<A>>
|
||||
|
||||
/**
|
||||
* Represents an addition/removal of a state machine.
|
||||
|
@ -11,9 +11,13 @@ import com.google.common.collect.HashMultimap
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowInfo
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
@ -262,26 +266,28 @@ class StateMachineManagerImpl(
|
||||
}
|
||||
|
||||
private fun onSessionMessage(message: ReceivedMessage) {
|
||||
val peer = message.peer
|
||||
val sessionMessage = try {
|
||||
message.data.deserialize<SessionMessage>()
|
||||
} catch (ex: Exception) {
|
||||
logger.error("Received corrupt SessionMessage data from ${message.peer}")
|
||||
logger.error("Received corrupt SessionMessage data from $peer")
|
||||
return
|
||||
}
|
||||
val sender = serviceHub.networkMapCache.getPeerByLegalName(message.peer)
|
||||
val sender = serviceHub.networkMapCache.getPeerByLegalName(peer)
|
||||
if (sender != null) {
|
||||
when (sessionMessage) {
|
||||
is ExistingSessionMessage -> onExistingSessionMessage(sessionMessage, sender)
|
||||
is SessionInit -> onSessionInit(sessionMessage, message, sender)
|
||||
}
|
||||
} else {
|
||||
logger.error("Unknown peer ${message.peer} in $sessionMessage")
|
||||
logger.error("Unknown peer $peer in $sessionMessage")
|
||||
}
|
||||
}
|
||||
|
||||
private fun onExistingSessionMessage(message: ExistingSessionMessage, sender: Party) {
|
||||
val session = openSessions[message.recipientSessionId]
|
||||
if (session != null) {
|
||||
session.fiber.pushToLoggingContext()
|
||||
session.fiber.logger.trace { "Received $message on $session from $sender" }
|
||||
if (session.retryable) {
|
||||
if (message is SessionConfirm && session.state is FlowSessionState.Initiated) {
|
||||
@ -327,6 +333,7 @@ class StateMachineManagerImpl(
|
||||
}
|
||||
|
||||
private fun onSessionInit(sessionInit: SessionInit, receivedMessage: ReceivedMessage, sender: Party) {
|
||||
|
||||
logger.trace { "Received $sessionInit from $sender" }
|
||||
val senderSessionId = sessionInit.initiatorSessionId
|
||||
|
||||
@ -350,9 +357,10 @@ class StateMachineManagerImpl(
|
||||
session.receivedMessages += ReceivedSessionMessage(sender, SessionData(session.ourSessionId, sessionInit.firstPayload))
|
||||
}
|
||||
openSessions[session.ourSessionId] = session
|
||||
// TODO Perhaps the session-init will specificy which of our multiple identies to use, which we would have to
|
||||
// double-check is actually ours. However, what if we want to control how our identities gets used?
|
||||
val fiber = createFiber(flow, FlowInitiator.Peer(sender))
|
||||
val context = InvocationContext.peer(sender.name)
|
||||
val fiber = createFiber(flow, context)
|
||||
fiber.pushToLoggingContext()
|
||||
logger.info("Accepting flow session from party ${sender.name}. Session id for tracing purposes is ${sessionInit.initiatorSessionId}.")
|
||||
flowSession.sessionFlow = flow
|
||||
flowSession.stateMachine = fiber
|
||||
fiber.openSessions[Pair(flow, sender)] = session
|
||||
@ -407,13 +415,13 @@ class StateMachineManagerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> createFiber(logic: FlowLogic<T>, flowInitiator: FlowInitiator, ourIdentity: Party? = null): FlowStateMachineImpl<T> {
|
||||
private fun <T> createFiber(logic: FlowLogic<T>, context: InvocationContext, ourIdentity: Party? = null): FlowStateMachineImpl<T> {
|
||||
val fsm = FlowStateMachineImpl(
|
||||
StateMachineRunId.createRandom(),
|
||||
logic,
|
||||
scheduler,
|
||||
flowInitiator,
|
||||
ourIdentity ?: serviceHub.myInfo.legalIdentities[0])
|
||||
ourIdentity ?: serviceHub.myInfo.legalIdentities[0],
|
||||
context)
|
||||
initFiber(fsm)
|
||||
return fsm
|
||||
}
|
||||
@ -423,7 +431,7 @@ class StateMachineManagerImpl(
|
||||
fiber.database = database
|
||||
fiber.serviceHub = serviceHub
|
||||
fiber.ourIdentityAndCert = serviceHub.myInfo.legalIdentitiesAndCerts.find { it.party == fiber.ourIdentity }
|
||||
?: throw IllegalStateException("Identity specified by ${fiber.id} (${fiber.ourIdentity}) is not one of ours!")
|
||||
?: throw IllegalStateException("Identity specified by ${fiber.id} (${fiber.ourIdentity.name}) is not one of ours!")
|
||||
fiber.actionOnSuspend = { ioRequest ->
|
||||
updateCheckpoint(fiber)
|
||||
// We commit on the fibers transaction that was copied across ThreadLocals during suspend
|
||||
@ -469,7 +477,7 @@ class StateMachineManagerImpl(
|
||||
private fun endAllFiberSessions(fiber: FlowStateMachineImpl<*>, result: Try<*>, propagated: Boolean) {
|
||||
openSessions.values.removeIf { session ->
|
||||
if (session.fiber == fiber) {
|
||||
session.endSession((result as? Try.Failure)?.exception, propagated)
|
||||
session.endSession(fiber.context, (result as? Try.Failure)?.exception, propagated)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@ -477,7 +485,7 @@ class StateMachineManagerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun FlowSessionInternal.endSession(exception: Throwable?, propagated: Boolean) {
|
||||
private fun FlowSessionInternal.endSession(context: InvocationContext, exception: Throwable?, propagated: Boolean) {
|
||||
val initiatedState = state as? FlowSessionState.Initiated ?: return
|
||||
val sessionEnd = if (exception == null) {
|
||||
NormalSessionEnd(initiatedState.peerSessionId)
|
||||
@ -502,11 +510,11 @@ class StateMachineManagerImpl(
|
||||
*
|
||||
* Note that you must be on the [executor] thread.
|
||||
*/
|
||||
override fun <A> startFlow(flowLogic: FlowLogic<A>, flowInitiator: FlowInitiator, ourIdentity: Party?): CordaFuture<FlowStateMachine<A>> {
|
||||
override fun <A> startFlow(flowLogic: FlowLogic<A>, context: InvocationContext): CordaFuture<FlowStateMachine<A>> {
|
||||
// TODO: Check that logic has @Suspendable on its call method.
|
||||
executor.checkOnThread()
|
||||
val fiber = database.transaction {
|
||||
val fiber = createFiber(flowLogic, flowInitiator, ourIdentity)
|
||||
val fiber = createFiber(flowLogic, context)
|
||||
updateCheckpoint(fiber)
|
||||
fiber
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.core.utilities.trace
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import org.hibernate.query.criteria.internal.expression.LiteralExpression
|
||||
@ -215,7 +214,7 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
|
||||
// state references
|
||||
criteria.stateRefs?.let {
|
||||
val persistentStateRefs = (criteria.stateRefs as List<StateRef>).map { PersistentStateRef(it.txhash.bytes.toHexString(), it.index) }
|
||||
val persistentStateRefs = (criteria.stateRefs as List<StateRef>).map(::PersistentStateRef)
|
||||
val compositeKey = vaultStates.get<PersistentStateRef>("stateRef")
|
||||
predicateSet.add(criteriaBuilder.and(compositeKey.`in`(persistentStateRefs)))
|
||||
}
|
||||
|
@ -5,15 +5,12 @@ import co.paralleluniverse.strands.Strand
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.StateLoader
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.node.services.StatesNotAvailableException
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.node.services.vault.SortAttribute
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.node.services.VaultQueryException
|
||||
import net.corda.core.node.services.vault.*
|
||||
import net.corda.core.schemas.PersistentStateRef
|
||||
@ -219,7 +216,7 @@ class NodeVaultService(
|
||||
val criteriaQuery = criteriaBuilder.createQuery(VaultSchemaV1.VaultStates::class.java)
|
||||
val vaultStates = criteriaQuery.from(VaultSchemaV1.VaultStates::class.java)
|
||||
val statusPredicate = criteriaBuilder.equal(vaultStates.get<Vault.StateStatus>(VaultSchemaV1.VaultStates::stateStatus.name), Vault.StateStatus.UNCONSUMED)
|
||||
val persistentStateRefs = refs.map { PersistentStateRef(it.txhash.bytes.toHexString(), it.index) }
|
||||
val persistentStateRefs = refs.map(::PersistentStateRef)
|
||||
val compositeKey = vaultStates.get<PersistentStateRef>(VaultSchemaV1.VaultStates::stateRef.name)
|
||||
val stateRefsPredicate = criteriaBuilder.and(compositeKey.`in`(persistentStateRefs))
|
||||
criteriaQuery.where(statusPredicate, stateRefsPredicate)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.node.shell
|
||||
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.StateMachineUpdate.Added
|
||||
import net.corda.core.messaging.StateMachineUpdate.Removed
|
||||
@ -72,7 +72,7 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
table.add(RowElement().add(
|
||||
LabelElement(formatFlowId(smmUpdate.id)),
|
||||
LabelElement(formatFlowName(smmUpdate.stateMachineInfo.flowLogicClassName)),
|
||||
LabelElement(formatFlowInitiator(smmUpdate.stateMachineInfo.initiator)),
|
||||
LabelElement(formatInvocationContext(smmUpdate.stateMachineInfo.context())),
|
||||
LabelElement("In progress")
|
||||
).style(stateColor(smmUpdate).fg()))
|
||||
indexMap[smmUpdate.id] = table.rows.size - 1
|
||||
@ -105,14 +105,8 @@ class FlowWatchPrintingSubscriber(private val toStream: RenderPrintWriter) : Sub
|
||||
return flowId.toString().removeSurrounding("[", "]")
|
||||
}
|
||||
|
||||
private fun formatFlowInitiator(flowInitiator: FlowInitiator): String {
|
||||
return when (flowInitiator) {
|
||||
is FlowInitiator.Scheduled -> flowInitiator.scheduledState.ref.toString()
|
||||
is FlowInitiator.Shell -> "Shell" // TODO Change when we will have more information on shell user.
|
||||
is FlowInitiator.Peer -> flowInitiator.party.name.organisation
|
||||
is FlowInitiator.RPC -> "RPC: " + flowInitiator.username
|
||||
is FlowInitiator.Service -> "Service: " + flowInitiator.name
|
||||
}
|
||||
private fun formatInvocationContext(context: InvocationContext): String {
|
||||
return context.principal().name
|
||||
}
|
||||
|
||||
private fun formatFlowResult(flowResult: Try<*>): String {
|
||||
|
@ -12,7 +12,6 @@ import net.corda.client.jackson.StringToMethodCallParser
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.OpenFuture
|
||||
@ -25,12 +24,11 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
|
||||
import net.corda.node.services.messaging.RpcContext
|
||||
import net.corda.node.services.messaging.RpcAuthContext
|
||||
import net.corda.node.services.messaging.RpcPermissions
|
||||
import net.corda.node.services.statemachine.FlowStateMachineImpl
|
||||
import net.corda.node.utilities.ANSIProgressRenderer
|
||||
import net.corda.node.utilities.CordaPersistence
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent
|
||||
import net.corda.nodeapi.User
|
||||
import org.crsh.command.InvocationContext
|
||||
import org.crsh.console.jline.JLineProcessor
|
||||
import org.crsh.console.jline.TerminalFactory
|
||||
@ -130,7 +128,9 @@ object InteractiveShell {
|
||||
InterruptHandler { jlineProcessor.interrupt() }.install()
|
||||
thread(name = "Command line shell processor", isDaemon = true) {
|
||||
// Give whoever has local shell access administrator access to the node.
|
||||
CURRENT_RPC_CONTEXT.set(RpcContext(User(ArtemisMessagingComponent.NODE_USER, "", setOf())))
|
||||
// TODO remove this after Shell switches to RPC
|
||||
val context = RpcAuthContext(net.corda.core.context.InvocationContext.shell(), RpcPermissions.NONE)
|
||||
CURRENT_RPC_CONTEXT.set(context)
|
||||
Emoji.renderIfSupported {
|
||||
jlineProcessor.run()
|
||||
}
|
||||
@ -235,7 +235,8 @@ object InteractiveShell {
|
||||
val clazz: Class<FlowLogic<*>> = uncheckedCast(matches.single())
|
||||
try {
|
||||
// TODO Flow invocation should use startFlowDynamic.
|
||||
val fsm = runFlowFromString({ node.services.startFlow(it, FlowInitiator.Shell).getOrThrow() }, inputData, clazz)
|
||||
val context = net.corda.core.context.InvocationContext.shell()
|
||||
val fsm = runFlowFromString({ node.services.startFlow(it, context).getOrThrow() }, inputData, clazz)
|
||||
// Show the progress tracker on the console until the flow completes or is interrupted with a
|
||||
// Ctrl-C keypress.
|
||||
val latch = CountDownLatch(1)
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.Issued
|
||||
@ -25,17 +26,14 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.internal.SecureCordaRPCOps
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.Permissions.Companion.startFlow
|
||||
import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT
|
||||
import net.corda.node.services.messaging.RpcContext
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.expect
|
||||
import net.corda.testing.expectEvents
|
||||
import net.corda.node.services.messaging.RpcAuthContext
|
||||
import net.corda.node.services.messaging.RpcPermissions
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.sequence
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
@ -62,14 +60,12 @@ class CordaRPCOpsImplTest {
|
||||
private lateinit var transactions: Observable<SignedTransaction>
|
||||
private lateinit var vaultTrackCash: Observable<Vault.Update<Cash.State>>
|
||||
|
||||
private val user = User("user", "pwd", permissions = emptySet())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
aliceNode = mockNet.createNode()
|
||||
rpc = SecureCordaRPCOps(aliceNode.services, aliceNode.smm, aliceNode.database, aliceNode.services)
|
||||
CURRENT_RPC_CONTEXT.set(RpcContext(user))
|
||||
CURRENT_RPC_CONTEXT.set(RpcAuthContext(InvocationContext.rpc(testActor()), RpcPermissions.NONE))
|
||||
|
||||
mockNet.runNetwork()
|
||||
withPermissions(invokeRpc(CordaRPCOps::notaryIdentities)) {
|
||||
@ -290,7 +286,7 @@ class CordaRPCOpsImplTest {
|
||||
|
||||
val previous = CURRENT_RPC_CONTEXT.get()
|
||||
try {
|
||||
CURRENT_RPC_CONTEXT.set(RpcContext(user.copy(permissions = permissions.toSet())))
|
||||
CURRENT_RPC_CONTEXT.set(previous.copy(grantedPermissions = RpcPermissions(permissions.toSet())))
|
||||
action.invoke()
|
||||
} finally {
|
||||
CURRENT_RPC_CONTEXT.set(previous)
|
||||
|
@ -1,9 +1,10 @@
|
||||
package net.corda.node.internal
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByService
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.node.AppServiceHub
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.CordaService
|
||||
@ -24,18 +25,18 @@ import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@StartableByService
|
||||
class DummyServiceFlow : FlowLogic<FlowInitiator>() {
|
||||
class DummyServiceFlow : FlowLogic<InvocationContext>() {
|
||||
companion object {
|
||||
object TEST_STEP : ProgressTracker.Step("Custom progress step")
|
||||
}
|
||||
override val progressTracker: ProgressTracker = ProgressTracker(TEST_STEP)
|
||||
|
||||
@Suspendable
|
||||
override fun call(): FlowInitiator {
|
||||
override fun call(): InvocationContext {
|
||||
// We call a subFlow, otehrwise there is no chance to subscribe to the ProgressTracker
|
||||
subFlow(CashIssueFlow(100.DOLLARS, OpaqueBytes.of(1), serviceHub.networkMapCache.notaryIdentities.first()))
|
||||
progressTracker.currentStep = TEST_STEP
|
||||
return stateMachine.flowInitiator
|
||||
return stateMachine.context
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,9 +44,8 @@ class DummyServiceFlow : FlowLogic<FlowInitiator>() {
|
||||
class TestCordaService(val appServiceHub: AppServiceHub): SingletonSerializeAsToken() {
|
||||
fun startServiceFlow() {
|
||||
val handle = appServiceHub.startFlow(DummyServiceFlow())
|
||||
val initiator = handle.returnValue.get()
|
||||
initiator as FlowInitiator.Service
|
||||
assertEquals(this.javaClass.name, initiator.serviceClassName)
|
||||
val context = handle.returnValue.get()
|
||||
assertEquals(this.javaClass.name, (context.origin as Origin.Service).serviceClassName)
|
||||
}
|
||||
|
||||
fun startServiceFlowAndTrack() {
|
||||
|
@ -77,6 +77,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
private lateinit var smmHasRemovedAllFlows: CountDownLatch
|
||||
private lateinit var kms: MockKeyManagementService
|
||||
private lateinit var mockSMM: StateMachineManager
|
||||
private val ourIdentity = ALICE_NAME
|
||||
var calls: Int = 0
|
||||
|
||||
/**
|
||||
|
@ -3,8 +3,12 @@ package net.corda.node.services.events
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowLogicRefFactory
|
||||
import net.corda.core.flows.SchedulableFlow
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM
|
||||
@ -20,6 +24,7 @@ import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
@ -107,8 +112,8 @@ class ScheduledFlowTests {
|
||||
var countScheduledFlows = 0
|
||||
nodeA.smm.track().updates.subscribe {
|
||||
if (it is StateMachineManager.Change.Add) {
|
||||
val initiator = it.logic.stateMachine.flowInitiator
|
||||
if (initiator is FlowInitiator.Scheduled)
|
||||
val context = it.logic.stateMachine.context
|
||||
if (context.origin is Origin.Scheduled)
|
||||
countScheduledFlows++
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.node.services.messaging
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
@ -14,7 +13,6 @@ import net.corda.node.services.api.MonitoringService
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||
import net.corda.node.services.network.NetworkMapCacheImpl
|
||||
import net.corda.node.services.network.NetworkMapClient
|
||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||
|
@ -9,8 +9,10 @@ import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.node.services.PartyInfo
|
||||
import net.corda.core.node.services.queryBy
|
||||
@ -505,7 +507,7 @@ class FlowFrameworkTests {
|
||||
|
||||
val committerFiber = aliceNode.registerFlowFactory(WaitingFlows.Waiter::class) {
|
||||
WaitingFlows.Committer(it)
|
||||
}.map { it.stateMachine }
|
||||
}.map { it.stateMachine }.map { uncheckedCast<FlowStateMachine<*>, FlowStateMachine<Any>>(it) }
|
||||
val waiterStx = bobNode.services.startFlow(WaitingFlows.Waiter(stx, alice)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThat(waiterStx.getOrThrow()).isEqualTo(committerFiber.getOrThrow().resultFuture.getOrThrow())
|
||||
|
@ -20,6 +20,7 @@ import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.singleIdentity
|
||||
import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -15,13 +15,10 @@ import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.issueInvalidState
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.MEGA_CORP_KEY
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -30,6 +30,7 @@ import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.rigorousMock
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
|
@ -25,6 +25,7 @@ import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.testing.DEV_TRUST_ROOT
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import rx.Observable
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
|
@ -5,10 +5,18 @@ package net.corda.testing
|
||||
import com.nhaarman.mockito_kotlin.doCallRealMethod
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.context.Origin
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.config.CertChainPolicyConfig
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.VerifierType
|
||||
@ -16,7 +24,6 @@ import net.corda.nodeapi.User
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
@ -73,3 +80,18 @@ fun testNodeConfiguration(
|
||||
doCallRealMethod().whenever(it).nodeKeystore
|
||||
}
|
||||
}
|
||||
|
||||
fun testActor(owningLegalIdentity: CordaX500Name = CordaX500Name("Test Company Inc.", "London", "GB")) = Actor(Actor.Id("Only For Testing"), AuthServiceId("TEST"), owningLegalIdentity)
|
||||
|
||||
fun testContext(owningLegalIdentity: CordaX500Name = CordaX500Name("Test Company Inc.", "London", "GB")) = InvocationContext.rpc(testActor(owningLegalIdentity))
|
||||
|
||||
/**
|
||||
* Starts an already constructed flow. Note that you must be on the server thread to call this method. [InvocationContext]
|
||||
* has origin [Origin.RPC] and actor with id "Only For Testing".
|
||||
*/
|
||||
fun <T> StartedNodeServices.startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = startFlow(logic, newContext()).getOrThrow()
|
||||
|
||||
/**
|
||||
* Creates a new [InvocationContext] for testing purposes.
|
||||
*/
|
||||
fun StartedNodeServices.newContext() = testContext(myInfo.chooseIdentity().name)
|
||||
|
@ -5,6 +5,8 @@ import net.corda.client.rpc.internal.KryoClientSerializationScheme
|
||||
import net.corda.client.rpc.internal.RPCClient
|
||||
import net.corda.client.rpc.internal.RPCClientConfiguration
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.context.AuthServiceId
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
@ -233,6 +235,7 @@ fun <A> rpcDriver(
|
||||
waitForNodesToFinish: Boolean = false,
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
externalTrace: Trace? = null,
|
||||
dsl: RPCDriverExposedDSLInterface.() -> A
|
||||
) = genericDriver(
|
||||
driverDsl = RPCDriverDSL(
|
||||
@ -247,7 +250,7 @@ fun <A> rpcDriver(
|
||||
waitForNodesToFinish = waitForNodesToFinish,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs
|
||||
)
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -274,7 +277,7 @@ private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityMan
|
||||
}
|
||||
|
||||
data class RPCDriverDSL(
|
||||
val driverDSL: DriverDSL
|
||||
private val driverDSL: DriverDSL, private val externalTrace: Trace?
|
||||
) : DriverDSLInternalInterface by driverDSL, RPCDriverInternalDSLInterface {
|
||||
private companion object {
|
||||
val notificationAddress = "notifications"
|
||||
@ -354,7 +357,7 @@ data class RPCDriverDSL(
|
||||
override fun <I : RPCOps> startInVmRpcClient(rpcOpsClass: Class<I>, username: String, password: String, configuration: RPCClientConfiguration): CordaFuture<I> {
|
||||
return driverDSL.executorService.fork {
|
||||
val client = RPCClient<I>(inVmClientTransportConfiguration, configuration)
|
||||
val connection = client.start(rpcOpsClass, username, password)
|
||||
val connection = client.start(rpcOpsClass, username, password, externalTrace)
|
||||
driverDSL.shutdownManager.registerShutdown {
|
||||
connection.close()
|
||||
}
|
||||
@ -398,7 +401,7 @@ data class RPCDriverDSL(
|
||||
): CordaFuture<I> {
|
||||
return driverDSL.executorService.fork {
|
||||
val client = RPCClient<I>(ArtemisTcpTransport.tcpTransport(ConnectionDirection.Outbound(), rpcAddress, null), configuration)
|
||||
val connection = client.start(rpcOpsClass, username, password)
|
||||
val connection = client.start(rpcOpsClass, username, password, externalTrace)
|
||||
driverDSL.shutdownManager.registerShutdown {
|
||||
connection.close()
|
||||
}
|
||||
@ -483,6 +486,7 @@ data class RPCDriverDSL(
|
||||
val userService = object : RPCUserService {
|
||||
override fun getUser(username: String): User? = if (username == rpcUser.username) rpcUser else null
|
||||
override val users: List<User> get() = listOf(rpcUser)
|
||||
override val id: AuthServiceId = AuthServiceId("RPC_DRIVER")
|
||||
}
|
||||
val rpcServer = RPCServer(
|
||||
ops,
|
||||
|
@ -5,7 +5,7 @@
|
||||
</Properties>
|
||||
<Appenders>
|
||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%-5level] %date{HH:mm:ss,SSS} [%t] (%F:%L) %c{2}.%method - %msg%n" />
|
||||
<PatternLayout pattern="[%-5level] %date{HH:mm:ss,SSS} [%t] (%F:%L) %c{2}.%method - %msg %X%n" />
|
||||
</Console>
|
||||
<!-- Required for printBasicInfo -->
|
||||
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
||||
|
Loading…
x
Reference in New Issue
Block a user