[CORDA-760]: Propagate invocation context across the codebase. (#2016)

This commit is contained in:
Michele Sollecito 2017-11-15 14:58:43 +00:00 committed by GitHub
parent f0a5ea96e7
commit 92c8861802
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1177 additions and 313 deletions

View File

@ -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

View File

@ -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)))
}
)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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))
}
/**

View File

@ -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))

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/>

View File

@ -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)

View 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" }
}
}

View 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)
}
}
}

View File

@ -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
/**

View File

@ -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"
}

View File

@ -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

View File

@ -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>

View 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" } ?: "")
}
}

View File

@ -0,0 +1,10 @@
package net.corda.core.utilities
import java.util.*
class UuidGenerator {
companion object {
fun next() : UUID = UUID.randomUUID()
}
}

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) }
}
}

View File

@ -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>()
}
}

View File

@ -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()
}
}

View File

@ -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" }

View File

@ -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<*>>,

View File

@ -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)
}
}

View File

@ -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()
}

View File

@ -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())
}
}

View File

@ -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())
}
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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) {

View File

@ -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.

View File

@ -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
}

View File

@ -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)))
}

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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() {

View File

@ -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
/**

View File

@ -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++
}
}

View File

@ -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

View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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.*

View File

@ -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.*

View File

@ -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)

View File

@ -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,

View File

@ -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">