mirror of
https://github.com/corda/corda.git
synced 2025-01-11 15:32:49 +00:00
Merge remote-tracking branch 'open/master' into colljos-merge-171117
# Conflicts: # .idea/compiler.xml # build.gradle # client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt # docs/source/changelog.rst # node/src/integration-test/kotlin/net/corda/node/services/AttachmentLoadingTests.kt # node/src/main/kotlin/net/corda/node/internal/StartedNode.kt # node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt # samples/network-visualiser/build.gradle # samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt # testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt # testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt
This commit is contained in:
commit
7a9364c8d5
@ -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,17 +1533,20 @@ 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)
|
||||
@org.jetbrains.annotations.NotNull public abstract Set partiesFromName(String, boolean)
|
||||
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party partyFromKey(java.security.PublicKey)
|
||||
@org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)
|
||||
@org.jetbrains.annotations.NotNull public abstract List registeredFlows()
|
||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.DataFeed stateMachineRecordedTransactionMappingFeed()
|
||||
@org.jetbrains.annotations.NotNull public abstract List stateMachineRecordedTransactionMappingSnapshot()
|
||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.DataFeed stateMachinesFeed()
|
||||
@org.jetbrains.annotations.NotNull public abstract List stateMachinesSnapshot()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash uploadAttachment(java.io.InputStream)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash uploadAttachmentWithMetadata(java.io.InputStream, String, String)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.Vault$Page vaultQuery(Class)
|
||||
@net.corda.core.messaging.RPCReturnsObservables @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.Vault$Page vaultQueryBy(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.PageSpecification, net.corda.core.node.services.vault.Sort, Class)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.Vault$Page vaultQueryByCriteria(net.corda.core.node.services.vault.QueryCriteria, Class)
|
||||
@ -1436,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()
|
||||
##
|
||||
@ -1445,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()
|
||||
@ -1511,6 +1698,7 @@ 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()
|
||||
##
|
||||
@ -1526,12 +1714,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)
|
||||
@ -1546,9 +1736,16 @@ 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)
|
||||
@org.jetbrains.annotations.Nullable public abstract net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash)
|
||||
@org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)
|
||||
##
|
||||
public final class net.corda.core.node.services.AttachmentStorageKt extends java.lang.Object
|
||||
##
|
||||
@ -1642,6 +1839,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()
|
||||
@ -1837,6 +2039,76 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
public static net.corda.core.node.services.vault.AggregateFunctionType valueOf(String)
|
||||
public static net.corda.core.node.services.vault.AggregateFunctionType[] values()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.node.services.vault.AttachmentQueryCriteria extends java.lang.Object implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria, net.corda.core.node.services.vault.GenericQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria and(net.corda.core.node.services.vault.AttachmentQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria or(net.corda.core.node.services.vault.AttachmentQueryCriteria)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.AttachmentQueryCriteria$AndComposition extends net.corda.core.node.services.vault.AttachmentQueryCriteria implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$AndVisitor
|
||||
public <init>(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria extends net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
public <init>()
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.vault.ColumnPredicate)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.vault.ColumnPredicate)
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate component1()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate component2()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate component3()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria copy(net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.vault.ColumnPredicate, net.corda.core.node.services.vault.ColumnPredicate)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate getFilenameCondition()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate getUploadDateCondition()
|
||||
@org.jetbrains.annotations.Nullable public final net.corda.core.node.services.vault.ColumnPredicate getUploaderCondition()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.AttachmentQueryCriteria$OrComposition extends net.corda.core.node.services.vault.AttachmentQueryCriteria implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$OrVisitor
|
||||
public <init>(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.AttachmentQueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.services.vault.AttachmentSort extends net.corda.core.node.services.vault.BaseSort
|
||||
public <init>(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final Collection component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.AttachmentSort copy(Collection)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final Collection getColumns()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
public static final class net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute extends java.lang.Enum
|
||||
protected <init>(String, int, String)
|
||||
@org.jetbrains.annotations.NotNull public final String getColumnName()
|
||||
public static net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute valueOf(String)
|
||||
public static net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute[] values()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.AttachmentSort$AttachmentSortColumn extends java.lang.Object
|
||||
public <init>(net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute, net.corda.core.node.services.vault.Sort$Direction)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.Sort$Direction component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.AttachmentSort$AttachmentSortColumn copy(net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute, net.corda.core.node.services.vault.Sort$Direction)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.Sort$Direction getDirection()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.AttachmentSort$AttachmentSortAttribute getSortAttribute()
|
||||
public int hashCode()
|
||||
public String toString()
|
||||
##
|
||||
public interface net.corda.core.node.services.vault.AttachmentsQueryCriteriaParser extends net.corda.core.node.services.vault.BaseQueryCriteriaParser
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria)
|
||||
##
|
||||
public interface net.corda.core.node.services.vault.BaseQueryCriteriaParser
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parse(net.corda.core.node.services.vault.GenericQueryCriteria, net.corda.core.node.services.vault.BaseSort)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseAnd(net.corda.core.node.services.vault.GenericQueryCriteria, net.corda.core.node.services.vault.GenericQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseOr(net.corda.core.node.services.vault.GenericQueryCriteria, net.corda.core.node.services.vault.GenericQueryCriteria)
|
||||
##
|
||||
public abstract class net.corda.core.node.services.vault.BaseSort extends java.lang.Object
|
||||
public <init>()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.node.services.vault.BinaryComparisonOperator extends java.lang.Enum implements net.corda.core.node.services.vault.Operator
|
||||
protected <init>(String, int)
|
||||
public static net.corda.core.node.services.vault.BinaryComparisonOperator valueOf(String)
|
||||
@ -1874,6 +2146,8 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
@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)
|
||||
@ -1882,6 +2156,7 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
@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)
|
||||
@ -1898,6 +2173,7 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
@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)
|
||||
@ -2052,15 +2328,29 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
public static net.corda.core.node.services.vault.EqualityComparisonOperator valueOf(String)
|
||||
public static net.corda.core.node.services.vault.EqualityComparisonOperator[] values()
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.node.services.vault.IQueryCriteriaParser
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parse(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.Sort)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseAnd(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.QueryCriteria)
|
||||
public interface net.corda.core.node.services.vault.GenericQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection visit(net.corda.core.node.services.vault.BaseQueryCriteriaParser)
|
||||
##
|
||||
public static interface net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria and(net.corda.core.node.services.vault.GenericQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria or(net.corda.core.node.services.vault.GenericQueryCriteria)
|
||||
##
|
||||
public static interface net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$AndVisitor extends net.corda.core.node.services.vault.GenericQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection visit(net.corda.core.node.services.vault.BaseQueryCriteriaParser)
|
||||
##
|
||||
public static interface net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$OrVisitor extends net.corda.core.node.services.vault.GenericQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.vault.GenericQueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection visit(net.corda.core.node.services.vault.BaseQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.core.node.services.vault.IQueryCriteriaParser extends net.corda.core.node.services.vault.BaseQueryCriteriaParser
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$CommonQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$FungibleAssetQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$LinearStateQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$VaultCustomQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$VaultQueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection parseOr(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.QueryCriteria)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.node.services.vault.LikenessOperator extends java.lang.Enum implements net.corda.core.node.services.vault.Operator
|
||||
protected <init>(String, int)
|
||||
@ -2087,10 +2377,15 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
public final boolean isDefault()
|
||||
public String toString()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.node.services.vault.QueryCriteria extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.QueryCriteria and(net.corda.core.node.services.vault.QueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.QueryCriteria or(net.corda.core.node.services.vault.QueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public abstract Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser)
|
||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.node.services.vault.QueryCriteria extends java.lang.Object implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria, net.corda.core.node.services.vault.GenericQueryCriteria
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria and(net.corda.core.node.services.vault.QueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria or(net.corda.core.node.services.vault.QueryCriteria)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.QueryCriteria$AndComposition extends net.corda.core.node.services.vault.QueryCriteria implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$AndVisitor
|
||||
public <init>(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.QueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public abstract static class net.corda.core.node.services.vault.QueryCriteria$CommonQueryCriteria extends net.corda.core.node.services.vault.QueryCriteria
|
||||
public <init>()
|
||||
@ -2151,6 +2446,12 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
|
||||
public String toString()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.QueryCriteria$OrComposition extends net.corda.core.node.services.vault.QueryCriteria implements net.corda.core.node.services.vault.GenericQueryCriteria$ChainableQueryCriteria$OrVisitor
|
||||
public <init>(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.QueryCriteria)
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria getA()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.vault.QueryCriteria getB()
|
||||
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser)
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.node.services.vault.QueryCriteria$SoftLockingCondition extends java.lang.Object
|
||||
public <init>(net.corda.core.node.services.vault.QueryCriteria$SoftLockingType, List)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.QueryCriteria$SoftLockingType component1()
|
||||
@ -2234,7 +2535,7 @@ public final class net.corda.core.node.services.vault.QueryCriteriaUtils extends
|
||||
public static final int DEFAULT_PAGE_SIZE = 200
|
||||
public static final int MAX_PAGE_SIZE = 2147483647
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.services.vault.Sort extends java.lang.Object
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.services.vault.Sort extends net.corda.core.node.services.vault.BaseSort
|
||||
public <init>(Collection)
|
||||
@org.jetbrains.annotations.NotNull public final Collection component1()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.Sort copy(Collection)
|
||||
@ -2430,19 +2731,13 @@ public static final class net.corda.core.serialization.SerializationContext$UseC
|
||||
public static net.corda.core.serialization.SerializationContext$UseCase valueOf(String)
|
||||
public static net.corda.core.serialization.SerializationContext$UseCase[] values()
|
||||
##
|
||||
public final class net.corda.core.serialization.SerializationDefaults extends java.lang.Object implements net.corda.core.serialization.internal.SerializationEnvironment
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getP2P_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getRPC_CLIENT_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getRPC_SERVER_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationFactory getSERIALIZATION_FACTORY()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT()
|
||||
public void setCHECKPOINT_CONTEXT(net.corda.core.serialization.SerializationContext)
|
||||
public void setP2P_CONTEXT(net.corda.core.serialization.SerializationContext)
|
||||
public void setRPC_CLIENT_CONTEXT(net.corda.core.serialization.SerializationContext)
|
||||
public void setRPC_SERVER_CONTEXT(net.corda.core.serialization.SerializationContext)
|
||||
public void setSERIALIZATION_FACTORY(net.corda.core.serialization.SerializationFactory)
|
||||
public void setSTORAGE_CONTEXT(net.corda.core.serialization.SerializationContext)
|
||||
public final class net.corda.core.serialization.SerializationDefaults extends java.lang.Object
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getP2P_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getRPC_CLIENT_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getRPC_SERVER_CONTEXT()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationFactory getSERIALIZATION_FACTORY()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT()
|
||||
public static final net.corda.core.serialization.SerializationDefaults INSTANCE
|
||||
##
|
||||
public abstract class net.corda.core.serialization.SerializationFactory extends java.lang.Object
|
||||
@ -2538,8 +2833,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()
|
||||
@ -2599,6 +2896,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)
|
||||
@ -2639,6 +2937,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)
|
||||
@ -2654,6 +2953,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
|
||||
@ -2679,13 +2979,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)
|
||||
@ -2749,6 +3053,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)
|
||||
@ -2826,6 +3131,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)
|
||||
@ -3027,6 +3347,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)
|
||||
##
|
||||
@ -3202,6 +3529,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
|
||||
|
18
.idea/compiler.xml
generated
18
.idea/compiler.xml
generated
@ -16,6 +16,9 @@
|
||||
<module name="client_test" target="1.8" />
|
||||
<module name="confidential-identities_main" target="1.8" />
|
||||
<module name="confidential-identities_test" target="1.8" />
|
||||
<module name="corda-core_integrationTest" target="1.8" />
|
||||
<module name="corda-core_smokeTest" target="1.8" />
|
||||
<module name="corda-finance_integrationTest" target="1.8" />
|
||||
<module name="corda-project_main" target="1.8" />
|
||||
<module name="corda-project_test" target="1.8" />
|
||||
<module name="cordapp_integrationTest" target="1.8" />
|
||||
@ -37,6 +40,13 @@
|
||||
<module name="docs_source_example-code_main" target="1.8" />
|
||||
<module name="docs_source_example-code_test" target="1.8" />
|
||||
<module name="docs_test" target="1.8" />
|
||||
<module name="experimental-kryo-hook_main" target="1.8" />
|
||||
<module name="experimental-kryo-hook_test" target="1.8" />
|
||||
<module name="example-code_integrationTest" target="1.8" />
|
||||
<module name="example-code_main" target="1.8" />
|
||||
<module name="example-code_test" target="1.8" />
|
||||
<module name="experimental-kryo-hook_main" target="1.8" />
|
||||
<module name="experimental-kryo-hook_test" target="1.8" />
|
||||
<module name="experimental_main" target="1.8" />
|
||||
<module name="experimental_test" target="1.8" />
|
||||
<module name="explorer-capsule_main" target="1.6" />
|
||||
@ -50,10 +60,15 @@
|
||||
<module name="gradle-plugins-cordform-common_test" target="1.8" />
|
||||
<module name="graphs_main" target="1.8" />
|
||||
<module name="graphs_test" target="1.8" />
|
||||
<module name="irs-demo-cordapp_integrationTest" target="1.8" />
|
||||
<module name="intellij-plugin_main" target="1.8" />
|
||||
<module name="intellij-plugin_test" target="1.8" />
|
||||
<module name="irs-demo-cordapp_main" target="1.8" />
|
||||
<module name="irs-demo-cordapp_main~1" target="1.8" />
|
||||
<module name="irs-demo-cordapp_test" target="1.8" />
|
||||
<module name="irs-demo-cordapp_test~1" target="1.8" />
|
||||
<module name="irs-demo-web_main" target="1.8" />
|
||||
<module name="irs-demo-web_test" target="1.8" />
|
||||
<module name="irs-demo_integrationTest" target="1.8" />
|
||||
<module name="irs-demo_main" target="1.8" />
|
||||
<module name="irs-demo_test" target="1.8" />
|
||||
@ -113,6 +128,9 @@
|
||||
<module name="simm-valuation-demo_test" target="1.8" />
|
||||
<module name="smoke-test-utils_main" target="1.8" />
|
||||
<module name="smoke-test-utils_test" target="1.8" />
|
||||
<module name="source-example-code_integrationTest" target="1.8" />
|
||||
<module name="source-example-code_main" target="1.8" />
|
||||
<module name="source-example-code_test" target="1.8" />
|
||||
<module name="test-common_main" target="1.8" />
|
||||
<module name="test-common_test" target="1.8" />
|
||||
<module name="test-utils_main" target="1.8" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BankOfCordaDriverKt - Issue Web" type="JetRunConfigurationType" factoryName="Kotlin">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<extension name="krasa.grepconsole.plugin.runConfiguration.GrepRunConfigurationExtension" selectedProfileId="0" />
|
||||
<option name="MAIN_CLASS_NAME" value="net.corda.bank.BankOfCordaDriverKt" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--role ISSUE_CASH_WEB --quantity 100 --currency USD" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BankOfCordaDriverKt - Run Stack" type="JetRunConfigurationType" factoryName="Kotlin">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<extension name="krasa.grepconsole.plugin.runConfiguration.GrepRunConfigurationExtension" selectedProfileId="0" />
|
||||
<option name="MAIN_CLASS_NAME" value="net.corda.bank.BankOfCordaDriverKt" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--role ISSUER" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Explorer - demo nodes" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<extension name="krasa.grepconsole.plugin.runConfiguration.GrepRunConfigurationExtension" selectedProfileId="0" />
|
||||
<option name="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Explorer - demo nodes (simulation)" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<extension name="krasa.grepconsole.plugin.runConfiguration.GrepRunConfigurationExtension" selectedProfileId="0" />
|
||||
<option name="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-S" />
|
||||
|
@ -31,7 +31,7 @@ Corda is a decentralised database system in which nodes trust each other as litt
|
||||
1. Read the [Getting Started](https://docs.corda.net/getting-set-up.html) documentation
|
||||
2. Run the [Example CorDapp](https://docs.corda.net/tutorial-cordapp.html)
|
||||
3. Read about Corda's [Key Concepts](https://docs.corda.net/key-concepts.html)
|
||||
3. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-index.html)
|
||||
3. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-introduction.html)
|
||||
|
||||
## Useful links
|
||||
|
||||
|
@ -21,7 +21,7 @@ buildscript {
|
||||
ext.capsule_version = '1.0.1'
|
||||
|
||||
ext.asm_version = '0.5.3'
|
||||
ext.artemis_version = '2.1.0'
|
||||
ext.artemis_version = '2.4.0'
|
||||
ext.jackson_version = '2.9.2'
|
||||
ext.jetty_version = '9.4.7.v20170914'
|
||||
ext.jersey_version = '2.25'
|
||||
|
@ -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
|
||||
@ -64,7 +64,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
||||
invokeRpc(CordaRPCOps::stateMachinesFeed),
|
||||
invokeRpc(CordaRPCOps::networkMapFeed))
|
||||
)
|
||||
val aliceNodeHandle = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
val aliceNodeHandle = startNode(providedName = ALICE_NAME, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
aliceNode = aliceNodeHandle.nodeInfo
|
||||
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
|
||||
val monitor = NodeMonitorModel()
|
||||
@ -79,7 +79,7 @@ class NodeMonitorModelTest : IntegrationTest() {
|
||||
rpc = monitor.proxyObservable.value!!
|
||||
notaryParty = defaultNotaryIdentity
|
||||
|
||||
val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
val bobNodeHandle = startNode(providedName = BOB_NAME, rpcUsers = listOf(cashUser)).getOrThrow()
|
||||
bobNode = bobNodeHandle.nodeInfo
|
||||
val monitorBob = NodeMonitorModel()
|
||||
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
|
||||
@ -91,20 +91,20 @@ class NodeMonitorModelTest : IntegrationTest() {
|
||||
|
||||
@Test
|
||||
fun `network map update`() = setup {
|
||||
val charlieNode = newNode(CHARLIE.name)
|
||||
val charlieNode = newNode(CHARLIE_NAME)
|
||||
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
|
||||
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
|
||||
.expectEvents(isStrict = false) {
|
||||
sequence(
|
||||
// TODO : Add test for remove when driver DSL support individual node shutdown.
|
||||
expect { output: NetworkMapCache.MapChange ->
|
||||
require(output.node.chooseIdentity().name == ALICE.name) { "Expecting : ${ALICE.name}, Actual : ${output.node.chooseIdentity().name}" }
|
||||
require(output.node.legalIdentities.any { it.name == ALICE_NAME }) { "Expecting : ${ALICE_NAME}, Actual : ${output.node.legalIdentities.map(Party::name)}" }
|
||||
},
|
||||
expect { output: NetworkMapCache.MapChange ->
|
||||
require(output.node.chooseIdentity().name == BOB.name) { "Expecting : ${BOB.name}, Actual : ${output.node.chooseIdentity().name}" }
|
||||
require(output.node.legalIdentities.any { it.name == BOB_NAME }) { "Expecting : ${BOB_NAME}, Actual : ${output.node.legalIdentities.map(Party::name)}" }
|
||||
},
|
||||
expect { output: NetworkMapCache.MapChange ->
|
||||
require(output.node.chooseIdentity().name == CHARLIE.name) { "Expecting : ${CHARLIE.name}, Actual : ${output.node.chooseIdentity().name}" }
|
||||
require(output.node.legalIdentities.any { it.name == CHARLIE_NAME }) { "Expecting : ${CHARLIE_NAME}, Actual : ${output.node.legalIdentities.map(Party::name)}" }
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -148,8 +148,8 @@ class NodeMonitorModelTest : IntegrationTest() {
|
||||
// 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 : IntegrationTest() {
|
||||
// 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 : IntegrationTest() {
|
||||
sequence(
|
||||
// MOVE
|
||||
expect { add: StateMachineUpdate.Added ->
|
||||
val initiator = add.stateMachineInfo.initiator
|
||||
require(initiator is FlowInitiator.Peer && aliceNode.isLegalIdentity(initiator.party))
|
||||
val context = add.stateMachineInfo.context()
|
||||
require(context.origin is Origin.Peer && aliceNode.isLegalIdentity(aliceNode.identityFromX500Name((context.origin as Origin.Peer).party)))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
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.identity.Party
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.messaging.*
|
||||
@ -20,10 +21,10 @@ 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
|
||||
@ -41,18 +42,20 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
invokeRpc("vaultQueryByCriteria"))
|
||||
)
|
||||
private lateinit var node: StartedNode<Node>
|
||||
private lateinit var identity: Party
|
||||
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
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
node = startNode(ALICE.name, rpcUsers = listOf(rpcUser))
|
||||
node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
|
||||
client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
|
||||
identity = node.info.identityFromX500Name(ALICE_NAME)
|
||||
}
|
||||
|
||||
@After
|
||||
@ -86,7 +89,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
println("Creating proxy")
|
||||
println("Starting flow")
|
||||
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow,
|
||||
20.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity()
|
||||
20.DOLLARS, OpaqueBytes.of(0), identity
|
||||
)
|
||||
println("Started flow, waiting on result")
|
||||
flowHandle.progress.subscribe {
|
||||
@ -98,7 +101,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
@Test
|
||||
fun `sub-type of FlowException thrown by flow`() {
|
||||
login(rpcUser.username, rpcUser.password)
|
||||
val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity())
|
||||
val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, identity)
|
||||
assertThatExceptionOfType(CashException::class.java).isThrownBy {
|
||||
handle.returnValue.getOrThrow()
|
||||
}
|
||||
@ -107,7 +110,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
@Test
|
||||
fun `check basic flow has no progress`() {
|
||||
login(rpcUser.username, rpcUser.password)
|
||||
connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity()).use {
|
||||
connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, identity).use {
|
||||
assertFalse(it is FlowProgressHandle<*>)
|
||||
}
|
||||
}
|
||||
@ -120,7 +123,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
|
||||
assertTrue(startCash.isEmpty(), "Should not start with any cash")
|
||||
|
||||
val flowHandle = proxy.startFlow(::CashIssueFlow,
|
||||
123.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity()
|
||||
123.DOLLARS, OpaqueBytes.of(0), identity
|
||||
)
|
||||
println("Started issuing cash, waiting on result")
|
||||
flowHandle.returnValue.get()
|
||||
@ -132,31 +135,50 @@ 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 updates = proxy.stateMachinesFeed().updates
|
||||
|
||||
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0),identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||
proxy.startFlow(::CashIssueFlow, 123.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
|
||||
proxy.startFlowDynamic(CashIssueFlow::class.java, 1000.DOLLARS, OpaqueBytes.of(0), identity).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)
|
||||
}
|
||||
)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkShellNotification(info: StateMachineInfo) {
|
||||
|
||||
val context = info.context()
|
||||
assertThat(context.origin).isInstanceOf(Origin.Shell::class.java)
|
||||
}
|
||||
|
||||
private fun checkRpcNotification(info: StateMachineInfo, rpcUsername: String, historicalIds: MutableSet<Trace.InvocationId>, externalTrace: Trace?, impersonatedActor: Actor?) {
|
||||
|
||||
val context = info.context()
|
||||
assertThat(context.origin).isInstanceOf(Origin.RPC::class.java)
|
||||
assertThat(context.externalTrace).isEqualTo(externalTrace)
|
||||
assertThat(context.impersonatedActor).isEqualTo(impersonatedActor)
|
||||
assertThat(context.actor?.id?.value).isEqualTo(rpcUsername)
|
||||
assertThat(historicalIds).doesNotContain(context.trace.invocationId)
|
||||
historicalIds.add(context.trace.invocationId)
|
||||
}
|
||||
|
@ -2,17 +2,22 @@ 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
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.NodeState
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.testing.*
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.driver.poll
|
||||
import net.corda.testing.internal.*
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
@ -236,6 +241,30 @@ class RPCStabilityTests : IntegrationTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clients receive notifications that node is shutting down`() {
|
||||
val alice = User("Alice", "Alice", setOf(invokeRpc(CordaRPCOps::nodeStateObservable)))
|
||||
val bob = User("Bob", "Bob", setOf(invokeRpc(CordaRPCOps::nodeStateObservable)))
|
||||
val slagathor = User("Slagathor", "Slagathor", setOf(invokeRpc(CordaRPCOps::nodeStateObservable)))
|
||||
val userList = listOf(alice, bob, slagathor)
|
||||
val expectedMessages = ArrayList<NodeState>()
|
||||
|
||||
rpcDriver(startNodesInProcess = true) {
|
||||
val node = startNode(rpcUsers = listOf(alice, bob, slagathor)).getOrThrow()
|
||||
userList.forEach {
|
||||
val connection = node.rpcClientToNode().start(it.username, it.password)
|
||||
val nodeStateObservable = connection.proxy.nodeStateObservable()
|
||||
nodeStateObservable.subscribe { update ->
|
||||
expectedMessages.add(update)
|
||||
}
|
||||
}
|
||||
|
||||
node.stop()
|
||||
}
|
||||
assertEquals(userList.size, expectedMessages.size)
|
||||
assertEquals(NodeState.SHUTTING_DOWN, expectedMessages.first())
|
||||
}
|
||||
|
||||
interface TrackSubscriberOps : RPCOps {
|
||||
fun subscribe(): Observable<Unit>
|
||||
}
|
||||
@ -320,9 +349,10 @@ class RPCStabilityTests : IntegrationTest() {
|
||||
val message = session.createMessage(false)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(
|
||||
clientAddress = SimpleString(myQueue),
|
||||
id = RPCApi.RpcRequestId(random63BitValue()),
|
||||
methodName = SlowConsumerRPCOps::streamAtInterval.name,
|
||||
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT).bytes
|
||||
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT).bytes,
|
||||
replyId = Trace.InvocationId.newInstance(),
|
||||
sessionId = Trace.SessionId.newInstance()
|
||||
)
|
||||
request.writeToClientMessage(message)
|
||||
producer.send(message)
|
||||
|
@ -3,11 +3,13 @@ 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
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
|
||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||
import java.time.Duration
|
||||
|
||||
@ -71,8 +73,15 @@ class CordaRPCClient @JvmOverloads constructor(
|
||||
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
|
||||
) {
|
||||
init {
|
||||
// TODO: allow clients to have serialization factory etc injected and align with RPC protocol version?
|
||||
KryoClientSerializationScheme.initialiseSerialization()
|
||||
try {
|
||||
effectiveSerializationEnv
|
||||
} catch (e: IllegalStateException) {
|
||||
try {
|
||||
KryoClientSerializationScheme.initialiseSerialization()
|
||||
} catch (e: IllegalStateException) {
|
||||
// Race e.g. two of these constructed in parallel, ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val rpcClient = RPCClient<CordaRPCOps>(
|
||||
@ -92,7 +101,22 @@ class CordaRPCClient @JvmOverloads constructor(
|
||||
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
|
||||
*/
|
||||
fun start(username: String, password: String): CordaRPCConnection {
|
||||
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password))
|
||||
return start(username, password, null, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in to the target server and returns an active connection. The returned connection is a [java.io.Closeable]
|
||||
* and can be used with a try-with-resources statement. If you don't use that, you should use the
|
||||
* [RPCConnection.notifyServerAndClose] or [RPCConnection.forceClose] methods to dispose of the connection object
|
||||
* when done.
|
||||
*
|
||||
* @param username The username to authenticate with.
|
||||
* @param password The password to authenticate with.
|
||||
* @param externalTrace external [Trace] for correlation.
|
||||
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
|
||||
*/
|
||||
fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?): CordaRPCConnection {
|
||||
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password, externalTrace, impersonatedActor))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,15 +2,18 @@ package net.corda.client.rpc.internal
|
||||
|
||||
import com.esotericsoftware.kryo.pool.KryoPool
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.nodeapi.internal.serialization.*
|
||||
import net.corda.nodeapi.internal.serialization.KRYO_P2P_CONTEXT
|
||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
|
||||
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
|
||||
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
|
||||
import net.corda.nodeapi.internal.serialization.kryo.RPCKryo
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
|
||||
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
|
||||
@ -19,7 +22,7 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
|
||||
|
||||
override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
|
||||
return KryoPool.Builder {
|
||||
DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context)).apply {
|
||||
DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context), publicKeySerializer).apply {
|
||||
classLoader = context.deserializationClassLoader
|
||||
}
|
||||
}.build()
|
||||
@ -29,25 +32,19 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
|
||||
override fun rpcServerKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException()
|
||||
|
||||
companion object {
|
||||
val isInitialised = AtomicBoolean(false)
|
||||
/** Call from main only. */
|
||||
fun initialiseSerialization() {
|
||||
if (!isInitialised.compareAndSet(false, true)) return
|
||||
try {
|
||||
SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
|
||||
registerScheme(KryoClientSerializationScheme())
|
||||
registerScheme(AMQPClientSerializationScheme())
|
||||
}
|
||||
SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT
|
||||
SerializationDefaults.RPC_CLIENT_CONTEXT = KRYO_RPC_CLIENT_CONTEXT
|
||||
} catch (e: IllegalStateException) {
|
||||
// Check that it's registered as we expect
|
||||
val factory = SerializationDefaults.SERIALIZATION_FACTORY
|
||||
val checkedFactory = factory as? SerializationFactoryImpl
|
||||
?: throw IllegalStateException("RPC client encountered conflicting configuration of serialization subsystem: $factory")
|
||||
check(checkedFactory.alreadyRegisteredSchemes.any { it is KryoClientSerializationScheme }) {
|
||||
"RPC client encountered conflicting configuration of serialization subsystem."
|
||||
}
|
||||
}
|
||||
nodeSerializationEnv = createSerializationEnv()
|
||||
}
|
||||
|
||||
fun createSerializationEnv(): SerializationEnvironment {
|
||||
return SerializationEnvironmentImpl(
|
||||
SerializationFactoryImpl().apply {
|
||||
registerScheme(KryoClientSerializationScheme())
|
||||
registerScheme(AMQPClientSerializationScheme())
|
||||
},
|
||||
KRYO_P2P_CONTEXT,
|
||||
rpcClientContext = KRYO_RPC_CLIENT_CONTEXT)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,15 @@ 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
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.minutes
|
||||
@ -99,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()}")
|
||||
@ -110,9 +115,10 @@ class RPCClient<I : RPCOps>(
|
||||
maxRetryInterval = rpcConfiguration.connectionMaxRetryInterval.toMillis()
|
||||
reconnectAttempts = rpcConfiguration.maxReconnectAttempts
|
||||
minLargeMessageSize = rpcConfiguration.maxFileSize
|
||||
isUseGlobalPools = nodeSerializationEnv != null
|
||||
}
|
||||
|
||||
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext)
|
||||
val sessionId = Trace.SessionId.newInstance()
|
||||
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext, sessionId, externalTrace, impersonatedActor)
|
||||
try {
|
||||
proxyHandler.start()
|
||||
val ops: I = uncheckedCast(Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), proxyHandler))
|
||||
|
@ -12,7 +12,9 @@ import com.google.common.util.concurrent.SettableFuture
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.client.rpc.RPCSinceVersion
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.context.Actor
|
||||
import net.corda.core.context.Trace
|
||||
import net.corda.core.context.Trace.InvocationId
|
||||
import net.corda.core.internal.LazyPool
|
||||
import net.corda.core.internal.LazyStickyPool
|
||||
import net.corda.core.internal.LifeCycle
|
||||
@ -24,7 +26,9 @@ import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisConsumer
|
||||
import net.corda.nodeapi.ArtemisProducer
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||
@ -35,6 +39,7 @@ import rx.Observable
|
||||
import rx.subjects.UnicastSubject
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
@ -70,7 +75,10 @@ class RPCClientProxyHandler(
|
||||
private val serverLocator: ServerLocator,
|
||||
private val clientAddress: SimpleString,
|
||||
private val rpcOpsClass: Class<out RPCOps>,
|
||||
serializationContext: SerializationContext
|
||||
serializationContext: SerializationContext,
|
||||
private val sessionId: Trace.SessionId,
|
||||
private val externalTrace: Trace?,
|
||||
private val impersonatedActor: Actor?
|
||||
) : InvocationHandler {
|
||||
|
||||
private enum class State {
|
||||
@ -127,14 +135,14 @@ class RPCClientProxyHandler(
|
||||
|
||||
// Stores the Observable IDs that are already removed from the map but are not yet sent to the server.
|
||||
private val observablesToReap = ThreadBox(object {
|
||||
var observables = ArrayList<RPCApi.ObservableId>()
|
||||
var observables = ArrayList<InvocationId>()
|
||||
})
|
||||
private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext)
|
||||
|
||||
private fun createRpcObservableMap(): RpcObservableMap {
|
||||
val onObservableRemove = RemovalListener<RPCApi.ObservableId, UnicastSubject<Notification<*>>> {
|
||||
val onObservableRemove = RemovalListener<InvocationId, UnicastSubject<Notification<*>>> {
|
||||
val observableId = it.key!!
|
||||
val rpcCallSite = callSiteMap?.remove(observableId.toLong)
|
||||
val rpcCallSite = callSiteMap?.remove(observableId)
|
||||
if (it.cause == RemovalCause.COLLECTED) {
|
||||
log.warn(listOf(
|
||||
"A hot observable returned from an RPC was never subscribed to.",
|
||||
@ -207,11 +215,12 @@ class RPCClientProxyHandler(
|
||||
if (sessionAndConsumer!!.session.isClosed) {
|
||||
throw RPCException("RPC Proxy is closed")
|
||||
}
|
||||
val rpcId = RPCApi.RpcRequestId(random63BitValue())
|
||||
callSiteMap?.set(rpcId.toLong, Throwable("<Call site of root RPC '${method.name}'>"))
|
||||
|
||||
val replyId = InvocationId.newInstance()
|
||||
callSiteMap?.set(replyId, Throwable("<Call site of root RPC '${method.name}'>"))
|
||||
try {
|
||||
val serialisedArguments = (arguments?.toList() ?: emptyList()).serialize(context = serializationContextWithObservableContext)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(clientAddress, rpcId, method.name, serialisedArguments.bytes)
|
||||
val request = RPCApi.ClientToServer.RpcRequest(clientAddress, method.name, serialisedArguments.bytes, replyId, sessionId, externalTrace, impersonatedActor)
|
||||
val replyFuture = SettableFuture.create<Any>()
|
||||
sessionAndProducerPool.run {
|
||||
val message = it.session.createMessage(false)
|
||||
@ -219,11 +228,11 @@ class RPCClientProxyHandler(
|
||||
|
||||
log.debug {
|
||||
val argumentsString = arguments?.joinToString() ?: ""
|
||||
"-> RPC($rpcId) -> ${method.name}($argumentsString): ${method.returnType}"
|
||||
"-> RPC(${replyId.value}) -> ${method.name}($argumentsString): ${method.returnType}"
|
||||
}
|
||||
|
||||
require(rpcReplyMap.put(rpcId, replyFuture) == null) {
|
||||
"Generated several RPC requests with same ID $rpcId"
|
||||
require(rpcReplyMap.put(replyId, replyFuture) == null) {
|
||||
"Generated several RPC requests with same ID $replyId"
|
||||
}
|
||||
it.producer.send(message)
|
||||
it.session.commit()
|
||||
@ -236,7 +245,7 @@ class RPCClientProxyHandler(
|
||||
// This must be a checked exception, so wrap it
|
||||
throw RPCException(e.message ?: "", e)
|
||||
} finally {
|
||||
callSiteMap?.remove(rpcId.toLong)
|
||||
callSiteMap?.remove(replyId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +263,7 @@ class RPCClientProxyHandler(
|
||||
when (result) {
|
||||
is Try.Success -> replyFuture.set(result.value)
|
||||
is Try.Failure -> {
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong)
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id)
|
||||
if (rpcCallSite != null) addRpcCallSiteToThrowable(result.exception, rpcCallSite)
|
||||
replyFuture.setException(result.exception)
|
||||
}
|
||||
@ -277,7 +286,7 @@ class RPCClientProxyHandler(
|
||||
}
|
||||
// Add call site information on error
|
||||
if (content.isOnError) {
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong)
|
||||
val rpcCallSite = callSiteMap?.get(serverToClient.id)
|
||||
if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite)
|
||||
}
|
||||
observable.onNext(content)
|
||||
@ -385,9 +394,9 @@ class RPCClientProxyHandler(
|
||||
}
|
||||
}
|
||||
|
||||
private typealias RpcObservableMap = Cache<RPCApi.ObservableId, UnicastSubject<Notification<*>>>
|
||||
private typealias RpcReplyMap = ConcurrentHashMap<RPCApi.RpcRequestId, SettableFuture<Any?>>
|
||||
private typealias CallSiteMap = ConcurrentHashMap<Long, Throwable?>
|
||||
private typealias RpcObservableMap = Cache<InvocationId, UnicastSubject<Notification<*>>>
|
||||
private typealias RpcReplyMap = ConcurrentHashMap<InvocationId, SettableFuture<Any?>>
|
||||
private typealias CallSiteMap = ConcurrentHashMap<InvocationId, Throwable?>
|
||||
|
||||
/**
|
||||
* Holds a context available during Kryo deserialisation of messages that are expected to contain Observables.
|
||||
@ -426,14 +435,14 @@ object RpcClientObservableSerializer : Serializer<Observable<*>>() {
|
||||
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<Observable<*>>): Observable<Any> {
|
||||
val observableContext = kryo.context[RpcObservableContextKey] as ObservableContext
|
||||
val observableId = RPCApi.ObservableId(input.readLong(true))
|
||||
val observableId = input.readInvocationId() ?: throw IllegalStateException("Unable to read invocationId from Input.")
|
||||
val observable = UnicastSubject.create<Notification<*>>()
|
||||
require(observableContext.observableMap.getIfPresent(observableId) == null) {
|
||||
"Multiple Observables arrived with the same ID $observableId"
|
||||
}
|
||||
val rpcCallSite = getRpcCallSite(kryo, observableContext)
|
||||
observableContext.observableMap.put(observableId, observable)
|
||||
observableContext.callSiteMap?.put(observableId.toLong, rpcCallSite)
|
||||
observableContext.callSiteMap?.put(observableId, rpcCallSite)
|
||||
// We pin all Observables into a hard reference store (rooted in the RPC proxy) on subscription so that users
|
||||
// don't need to store a reference to the Observables themselves.
|
||||
return pinInSubscriptions(observable, observableContext.hardReferenceStore).doOnUnsubscribe {
|
||||
@ -444,12 +453,19 @@ object RpcClientObservableSerializer : Serializer<Observable<*>>() {
|
||||
}.dematerialize()
|
||||
}
|
||||
|
||||
private fun Input.readInvocationId() : InvocationId? {
|
||||
|
||||
val value = readString() ?: return null
|
||||
val timestamp = readLong()
|
||||
return InvocationId(value, Instant.ofEpochMilli(timestamp))
|
||||
}
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, observable: Observable<*>) {
|
||||
throw UnsupportedOperationException("Cannot serialise Observables on the client side")
|
||||
}
|
||||
|
||||
private fun getRpcCallSite(kryo: Kryo, observableContext: ObservableContext): Throwable? {
|
||||
val rpcRequestOrObservableId = kryo.context[RPCApi.RpcRequestOrObservableIdKey] as Long
|
||||
val rpcRequestOrObservableId = kryo.context[RPCApi.RpcRequestOrObservableIdKey] as InvocationId
|
||||
return observableContext.callSiteMap?.get(rpcRequestOrObservableId)
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package net.corda.kotlin.rpc
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ValidateClasspathTest {
|
||||
@Test
|
||||
fun `node not on classpath`() {
|
||||
val paths = System.getProperty("java.class.path").split(File.pathSeparatorChar).map { Paths.get(it) }
|
||||
// First find core so that if node is there, it's in the form we expect:
|
||||
assertFalse(paths.filter { it.contains("core" / "build") }.isEmpty())
|
||||
assertTrue(paths.filter { it.contains("node" / "build") }.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Path.contains(that: Path): Boolean {
|
||||
val size = that.nameCount
|
||||
(0..nameCount - size).forEach {
|
||||
if (subpath(it, it + size) == that) return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -6,10 +6,10 @@ import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.rpcTestUser
|
||||
import net.corda.testing.startInVmRpcClient
|
||||
import net.corda.testing.startRpcClient
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcTestUser
|
||||
import net.corda.testing.internal.startInVmRpcClient
|
||||
import net.corda.testing.internal.startRpcClient
|
||||
import org.apache.activemq.artemis.api.core.client.ClientSession
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
|
@ -7,9 +7,9 @@ import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.messaging.rpcContext
|
||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.rpcDriver
|
||||
import net.corda.testing.rpcTestUser
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import net.corda.testing.internal.rpcTestUser
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@ -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
|
||||
|
@ -7,8 +7,8 @@ import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.rpcDriver
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
@ -5,8 +5,8 @@ import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.rpcDriver
|
||||
import net.corda.testing.startRpcClient
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import net.corda.testing.internal.startRpcClient
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -5,14 +5,14 @@ import net.corda.client.rpc.internal.RPCClientConfiguration
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.performance.div
|
||||
import net.corda.testing.internal.performance.div
|
||||
import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.testing.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.measure
|
||||
import net.corda.testing.performance.startPublishingFixedRateInjector
|
||||
import net.corda.testing.performance.startReporter
|
||||
import net.corda.testing.performance.startTightLoopInjector
|
||||
import net.corda.testing.rpcDriver
|
||||
import net.corda.testing.internal.performance.startPublishingFixedRateInjector
|
||||
import net.corda.testing.internal.performance.startReporter
|
||||
import net.corda.testing.internal.performance.startTightLoopInjector
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -4,10 +4,9 @@ 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.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.rpcDriver
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
@ -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
|
||||
|
@ -3,10 +3,12 @@ package net.corda.confidential
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.Before
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import org.junit.Test
|
||||
import kotlin.test.*
|
||||
|
||||
@ -83,28 +85,30 @@ class SwapIdentitiesFlowTests {
|
||||
val notaryNode = mockNet.defaultNotaryNode
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
val bob: Party = bobNode.services.myInfo.singleIdentity()
|
||||
val alice: PartyAndCertificate = aliceNode.info.singleIdentityAndCert()
|
||||
val bob: PartyAndCertificate = bobNode.info.singleIdentityAndCert()
|
||||
val notary: PartyAndCertificate = mockNet.defaultNotaryIdentityAndCert
|
||||
// Check that the wrong signature is rejected
|
||||
notaryNode.database.transaction {
|
||||
notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||
notaryNode.services.keyManagementService.freshKeyAndCert(notary, false)
|
||||
}.let { anonymousNotary ->
|
||||
val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousNotary)
|
||||
val signature = notaryNode.services.keyManagementService.sign(sigData, anonymousNotary.owningKey)
|
||||
assertFailsWith<SwapIdentitiesException>("Signature does not match the given identity and nonce") {
|
||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, anonymousNotary, signature.withoutKey())
|
||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob.party, anonymousNotary, signature.withoutKey())
|
||||
}
|
||||
}
|
||||
// Check that the right signing key, but wrong identity is rejected
|
||||
val anonymousAlice = aliceNode.database.transaction {
|
||||
aliceNode.services.keyManagementService.freshKeyAndCert(aliceNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||
val anonymousAlice: PartyAndCertificate = aliceNode.database.transaction {
|
||||
aliceNode.services.keyManagementService.freshKeyAndCert(alice, false)
|
||||
}
|
||||
bobNode.database.transaction {
|
||||
bobNode.services.keyManagementService.freshKeyAndCert(bobNode.services.myInfo.chooseIdentityAndCert(), false)
|
||||
bobNode.services.keyManagementService.freshKeyAndCert(bob, false)
|
||||
}.let { anonymousBob ->
|
||||
val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousAlice)
|
||||
val signature = bobNode.services.keyManagementService.sign(sigData, anonymousBob.owningKey)
|
||||
assertFailsWith<SwapIdentitiesException>("Signature does not match the given identity and nonce.") {
|
||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob, anonymousBob, signature.withoutKey())
|
||||
SwapIdentitiesFlow.validateAndRegisterIdentity(aliceNode.services.identityService, bob.party, anonymousBob, signature.withoutKey())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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/>
|
||||
|
@ -117,6 +117,9 @@ dependencies {
|
||||
|
||||
// JPA 2.1 annotations.
|
||||
compile "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
|
||||
|
||||
// required to use @Type annotation
|
||||
compile "org.hibernate:hibernate-core:$hibernate_version"
|
||||
}
|
||||
|
||||
// TODO Consider moving it to quasar-utils in the future (introduced with PR-1388)
|
||||
|
@ -0,0 +1,9 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
/**
|
||||
* Authentication / Authorisation Service ID.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class AuthServiceId(val value: String)
|
136
core/src/main/kotlin/net/corda/core/context/InvocationContext.kt
Normal file
136
core/src/main/kotlin/net/corda/core/context/InvocationContext.kt
Normal file
@ -0,0 +1,136 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.contracts.ScheduledStateRef
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.security.Principal
|
||||
|
||||
/**
|
||||
* Models the information needed to trace an invocation in Corda.
|
||||
* Includes initiating actor, origin, trace information, and optional external trace information to correlate clients' IDs.
|
||||
*
|
||||
* @param origin origin of the invocation.
|
||||
* @param trace Corda invocation trace.
|
||||
* @param actor acting agent of the invocation, used to derive the security principal.
|
||||
* @param externalTrace optional external invocation trace for cross-system logs correlation.
|
||||
* @param impersonatedActor optional impersonated actor, used for logging but not for authorisation.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class InvocationContext(val origin: Origin, val trace: Trace, val actor: Actor?, val externalTrace: Trace? = null, val impersonatedActor: Actor? = null) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(origin: Origin, trace: Trace = Trace.newInstance(), actor: Actor? = null, externalTrace: Trace? = null, impersonatedActor: Actor? = null) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.RPC] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun rpc(actor: Actor, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.RPC(actor), trace, actor, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Peer] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.Peer(party), trace, null, externalTrace, impersonatedActor)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Service] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Scheduled] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Scheduled(scheduledState), trace, null, externalTrace)
|
||||
|
||||
/**
|
||||
* Creates an [InvocationContext] with [Origin.Shell] origin.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(Origin.Shell, trace, null, externalTrace)
|
||||
}
|
||||
|
||||
/**
|
||||
* Associated security principal.
|
||||
*/
|
||||
fun principal(): Principal = origin.principal()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models an initiator in Corda, can be a user, a service, etc.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdentity: CordaX500Name) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name): Actor = Actor(Id(serviceClassName), AuthServiceId("SERVICE"), owningLegalIdentity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor id.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Id(val value: String)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Invocation origin for tracing purposes.
|
||||
*/
|
||||
@CordaSerializable
|
||||
sealed class Origin {
|
||||
|
||||
/**
|
||||
* Returns the [Principal] for a given [Actor].
|
||||
*/
|
||||
abstract fun principal(): Principal
|
||||
|
||||
/**
|
||||
* Origin was an RPC call.
|
||||
*/
|
||||
data class RPC(private val actor: Actor) : Origin() {
|
||||
|
||||
override fun principal() = Principal { actor.id.value }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a message sent by a [Peer].
|
||||
*/
|
||||
data class Peer(val party: CordaX500Name) : Origin() {
|
||||
|
||||
override fun principal() = Principal { party.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a Corda Service.
|
||||
*/
|
||||
data class Service(val serviceClassName: String, val owningLegalIdentity: CordaX500Name) : Origin() {
|
||||
|
||||
override fun principal() = Principal { serviceClassName }
|
||||
}
|
||||
|
||||
/**
|
||||
* Origin was a scheduled activity.
|
||||
*/
|
||||
data class Scheduled(val scheduledState: ScheduledStateRef) : Origin() {
|
||||
|
||||
override fun principal() = Principal { "Scheduler" }
|
||||
}
|
||||
|
||||
// TODO When proper ssh access enabled, add username/use RPC?
|
||||
/**
|
||||
* Origin was the Shell.
|
||||
*/
|
||||
object Shell : Origin() {
|
||||
|
||||
override fun principal() = Principal { "Shell User" }
|
||||
}
|
||||
}
|
56
core/src/main/kotlin/net/corda/core/context/Trace.kt
Normal file
56
core/src/main/kotlin/net/corda/core/context/Trace.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package net.corda.core.context
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.Id
|
||||
import net.corda.core.utilities.UuidGenerator
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Contextual tracing information, including invocation and session id.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Trace(val invocationId: InvocationId, val sessionId: SessionId) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a trace using a [InvocationId.newInstance] with default arguments and a [SessionId] matching the value and timestamp from the invocation id..
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(invocationId: InvocationId = InvocationId.newInstance(), sessionId: SessionId = SessionId(invocationId.value, invocationId.timestamp)) = Trace(invocationId, sessionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents id and timestamp of an invocation.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class InvocationId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
|
||||
|
||||
companion object {
|
||||
private val TYPE = "Invocation"
|
||||
|
||||
/**
|
||||
* Creates an invocation id using a [java.util.UUID] as value and [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = InvocationId(value, timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents id and timestamp of a session.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class SessionId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
|
||||
|
||||
companion object {
|
||||
private val TYPE = "Session"
|
||||
|
||||
/**
|
||||
* Creates a session id using a [java.util.UUID] as value and [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = SessionId(value, timestamp)
|
||||
}
|
||||
}
|
||||
}
|
@ -45,9 +45,17 @@ interface NamedByHash {
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Issued<out P : Any>(val issuer: PartyAndReference, val product: P) {
|
||||
init {
|
||||
require(issuer.reference.bytes.size <= MAX_ISSUER_REF_SIZE) { "Maximum issuer reference size is $MAX_ISSUER_REF_SIZE." }
|
||||
}
|
||||
override fun toString() = "$product issued by $issuer"
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum permissible size of an issuer reference.
|
||||
*/
|
||||
const val MAX_ISSUER_REF_SIZE = 512
|
||||
|
||||
/**
|
||||
* Strips the issuer and returns an [Amount] of the raw token directly. This is useful when you are mixing code that
|
||||
* cares about specific issuers with code that will accept any, or which is imposing issuer constraints via some
|
||||
@ -86,6 +94,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
|
||||
|
||||
/**
|
||||
|
@ -103,3 +103,5 @@ fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this)
|
||||
* Compute the SHA-256 hash for the contents of the [OpaqueBytes].
|
||||
*/
|
||||
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes)
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ class CollectSignaturesFlow @JvmOverloads constructor(val partiallySignedTx: Sig
|
||||
}
|
||||
|
||||
// The signatures must be valid and the transaction must be valid.
|
||||
partiallySignedTx.verifySignaturesExcept(*notSigned.toTypedArray())
|
||||
partiallySignedTx.verifySignaturesExcept(notSigned)
|
||||
partiallySignedTx.tx.toLedgerTransaction(serviceHub).verify()
|
||||
|
||||
// Determine who still needs to sign.
|
||||
@ -251,7 +251,7 @@ abstract class SignTransactionFlow(val otherSideSession: FlowSession,
|
||||
val signed = stx.sigs.map { it.by }
|
||||
val allSigners = stx.tx.requiredSigningKeys
|
||||
val notSigned = allSigners - signed
|
||||
stx.verifySignaturesExcept(*notSigned.toTypedArray())
|
||||
stx.verifySignaturesExcept(notSigned)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,29 +10,35 @@ import java.security.Principal
|
||||
* communication started by peer node [FlowInitiator.Peer], scheduled flows [FlowInitiator.Scheduled]
|
||||
* or via the Corda Shell [FlowInitiator.Shell].
|
||||
*/
|
||||
@Deprecated("Do not use these types. Future releases might remove them.")
|
||||
@CordaSerializable
|
||||
sealed class FlowInitiator : Principal {
|
||||
/** Started using [net.corda.core.messaging.CordaRPCOps.startFlowDynamic]. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class RPC(val username: String) : FlowInitiator() {
|
||||
override fun getName(): String = username
|
||||
}
|
||||
|
||||
/** Started when we get new session initiation request. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Peer(val party: Party) : FlowInitiator() {
|
||||
override fun getName(): String = party.name.toString()
|
||||
}
|
||||
|
||||
/** Started by a CordaService. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Service(val serviceClassName: String) : FlowInitiator() {
|
||||
override fun getName(): String = serviceClassName
|
||||
}
|
||||
|
||||
/** Started as scheduled activity. */
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() {
|
||||
override fun getName(): String = "Scheduler"
|
||||
}
|
||||
|
||||
// TODO When proper ssh access enabled, add username/use RPC?
|
||||
@Deprecated("Do not use this type. Future releases might remove it.")
|
||||
object Shell : FlowInitiator() {
|
||||
override fun getName(): String = "Shell User"
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
@ -55,7 +56,7 @@ interface FlowStateMachine<R> {
|
||||
val logger: Logger
|
||||
val id: StateMachineRunId
|
||||
val resultFuture: CordaFuture<R>
|
||||
val flowInitiator: FlowInitiator
|
||||
val context: InvocationContext
|
||||
val ourIdentityAndCert: PartyAndCertificate
|
||||
|
||||
@Suspendable
|
||||
|
@ -19,6 +19,8 @@ import rx.subjects.UnicastSubject
|
||||
import java.io.*
|
||||
import java.lang.reflect.Field
|
||||
import java.math.BigDecimal
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets.UTF_8
|
||||
import java.nio.file.*
|
||||
@ -303,3 +305,5 @@ fun TransactionBuilder.toLedgerTransaction(services: ServiceHub, serializationCo
|
||||
|
||||
/** Convenience method to get the package name of a class literal. */
|
||||
val KClass<*>.packageName get() = java.`package`.name
|
||||
|
||||
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection
|
||||
|
80
core/src/main/kotlin/net/corda/core/internal/ToggleField.kt
Normal file
80
core/src/main/kotlin/net/corda/core/internal/ToggleField.kt
Normal file
@ -0,0 +1,80 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import org.slf4j.Logger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/** May go from null to non-null and vice-versa, and that's it. */
|
||||
abstract class ToggleField<T>(val name: String) {
|
||||
private val writeMutex = Any() // Protects the toggle logic only.
|
||||
abstract fun get(): T?
|
||||
fun set(value: T?) = synchronized(writeMutex) {
|
||||
if (value != null) {
|
||||
check(get() == null) { "$name already has a value." }
|
||||
setImpl(value)
|
||||
} else {
|
||||
check(get() != null) { "$name is already null." }
|
||||
clear()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun setImpl(value: T)
|
||||
protected abstract fun clear()
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = get()
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) = set(value)
|
||||
}
|
||||
|
||||
class SimpleToggleField<T>(name: String, private val once: Boolean = false) : ToggleField<T>(name) {
|
||||
private val holder = AtomicReference<T?>() // Force T? in API for safety.
|
||||
override fun get() = holder.get()
|
||||
override fun setImpl(value: T) = holder.set(value)
|
||||
override fun clear() {
|
||||
check(!once) { "Value of $name cannot be changed." }
|
||||
holder.set(null)
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadLocalToggleField<T>(name: String) : ToggleField<T>(name) {
|
||||
private val threadLocal = ThreadLocal<T?>()
|
||||
override fun get() = threadLocal.get()
|
||||
override fun setImpl(value: T) = threadLocal.set(value)
|
||||
override fun clear() = threadLocal.remove()
|
||||
}
|
||||
|
||||
/** The named thread has leaked from a previous test. */
|
||||
class ThreadLeakException : RuntimeException("Leaked thread detected: ${Thread.currentThread().name}")
|
||||
|
||||
/** @param isAGlobalThreadBeingCreated whether a global thread (that should not inherit any value) is being created. */
|
||||
class InheritableThreadLocalToggleField<T>(name: String,
|
||||
private val log: Logger = loggerFor<InheritableThreadLocalToggleField<*>>(),
|
||||
private val isAGlobalThreadBeingCreated: (Array<StackTraceElement>) -> Boolean) : ToggleField<T>(name) {
|
||||
private inner class Holder(value: T) : AtomicReference<T?>(value) {
|
||||
fun valueOrDeclareLeak() = get() ?: throw ThreadLeakException()
|
||||
fun childValue(): Holder? {
|
||||
val e = ThreadLeakException() // Expensive, but so is starting the new thread.
|
||||
return if (isAGlobalThreadBeingCreated(e.stackTrace)) {
|
||||
get() ?: log.warn(e.message)
|
||||
null
|
||||
} else {
|
||||
get() ?: log.error(e.message)
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val threadLocal = object : InheritableThreadLocal<Holder?>() {
|
||||
override fun childValue(holder: InheritableThreadLocalToggleField<T>.Holder?): InheritableThreadLocalToggleField<T>.Holder? {
|
||||
// The Holder itself may be null due to prior events, a leak is not indicated in that case:
|
||||
return holder?.childValue()
|
||||
}
|
||||
}
|
||||
|
||||
override fun get() = threadLocal.get()?.valueOrDeclareLeak()
|
||||
override fun setImpl(value: T) = threadLocal.set(Holder(value))
|
||||
override fun clear() = threadLocal.run {
|
||||
val holder = get()!!
|
||||
remove()
|
||||
holder.set(null) // Threads that inherited the holder are now considered to have escaped from the test.
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* A write-once property to be used as delegate for Kotlin var properties. The expectation is that this is initialised
|
||||
* prior to the spawning of any threads that may access it and so there's no need for it to be volatile.
|
||||
*/
|
||||
class WriteOnceProperty<T : Any>(private val defaultValue: T? = null) {
|
||||
private var v: T? = defaultValue
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = v ?: throw IllegalStateException("Write-once property $property not set.")
|
||||
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
check(v == defaultValue || v === value) { "Cannot set write-once property $property more than once." }
|
||||
v = value
|
||||
}
|
||||
}
|
@ -57,6 +57,9 @@ fun <V, W> CordaFuture<out V>.flatMap(transform: (V) -> CordaFuture<out W>): Cor
|
||||
})
|
||||
}
|
||||
|
||||
/** Wrap a CompletableFuture, for example one that was returned by some API. */
|
||||
fun <V> CompletableFuture<V>.asCordaFuture(): CordaFuture<V> = CordaFutureImpl(this)
|
||||
|
||||
/**
|
||||
* If all of the given futures succeed, the returned future's outcome is a list of all their values.
|
||||
* The values are in the same order as the futures in the collection, not the order of completion.
|
||||
|
@ -27,8 +27,12 @@ object NodeInfoSchemaV1 : MappedSchema(
|
||||
@Column(name = "node_info_id")
|
||||
var id: Int,
|
||||
|
||||
@Column(name="node_info_hash", length = 64)
|
||||
val hash: String,
|
||||
|
||||
@Column(name = "addresses")
|
||||
@OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true)
|
||||
@JoinColumn(name = "node_info_id")
|
||||
val addresses: List<NodeInfoSchemaV1.DBHostAndPort>,
|
||||
|
||||
@Column(name = "legal_identities_certs")
|
||||
@ -66,6 +70,7 @@ object NodeInfoSchemaV1 : MappedSchema(
|
||||
) : Serializable
|
||||
|
||||
@Entity
|
||||
@Table(name = "node_info_hosts")
|
||||
data class DBHostAndPort(
|
||||
@EmbeddedId
|
||||
private val pk: PKHostAndPort
|
||||
@ -91,7 +96,6 @@ object NodeInfoSchemaV1 : MappedSchema(
|
||||
@Column(name = "party_name", nullable = false)
|
||||
val name: String,
|
||||
|
||||
@Lob
|
||||
@Column(name = "owning_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
val owningKeyHash: String,
|
||||
|
||||
|
@ -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
|
||||
@ -10,13 +14,11 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.VaultQueryException
|
||||
import net.corda.core.node.services.vault.DEFAULT_PAGE_SIZE
|
||||
import net.corda.core.node.services.vault.PageSpecification
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.node.services.vault.*
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.Try
|
||||
@ -25,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)"
|
||||
}
|
||||
|
||||
@ -196,6 +226,10 @@ interface CordaRPCOps : RPCOps {
|
||||
/** Returns Node's NodeInfo, assuming this will not change while the node is running. */
|
||||
fun nodeInfo(): NodeInfo
|
||||
|
||||
/** Returns and [Observable] object with future states of the node. */
|
||||
@RPCReturnsObservables
|
||||
fun nodeStateObservable(): Observable<NodeState>
|
||||
|
||||
/**
|
||||
* Returns network's notary identities, assuming this will not change while the node is running.
|
||||
*
|
||||
@ -218,6 +252,12 @@ interface CordaRPCOps : RPCOps {
|
||||
/** Uploads a jar to the node, returns it's hash. */
|
||||
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
|
||||
|
||||
/** Queries attachments metadata */
|
||||
fun queryAttachments(query: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId>
|
||||
|
||||
/** Returns the node's current time. */
|
||||
fun currentNodeTime(): Instant
|
||||
|
||||
@ -428,3 +468,8 @@ inline fun <T, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startTrac
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class DataFeed<out A, B>(val snapshot: A, val updates: Observable<B>)
|
||||
|
||||
@CordaSerializable
|
||||
enum class NodeState {
|
||||
SHUTTING_DOWN
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package net.corda.core.node
|
||||
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* @property minimumPlatformVersion
|
||||
* @property notaries
|
||||
* @property eventHorizon
|
||||
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
|
||||
* @property maxTransactionSize Maximum permitted transaction size in bytes.
|
||||
* @property modifiedTime
|
||||
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
|
||||
* of parameters.
|
||||
*/
|
||||
// TODO Wire up the parameters
|
||||
@CordaSerializable
|
||||
data class NetworkParameters(
|
||||
val minimumPlatformVersion: Int,
|
||||
val notaries: List<NotaryInfo>,
|
||||
val eventHorizon: Duration,
|
||||
val maxMessageSize: Int,
|
||||
val maxTransactionSize: Int,
|
||||
val modifiedTime: Instant,
|
||||
val epoch: Int
|
||||
) {
|
||||
init {
|
||||
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
|
||||
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
|
||||
require(epoch > 0) { "epoch must be at least 1" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class NotaryInfo(val identity: Party, val validating: Boolean)
|
@ -44,8 +44,21 @@ data class NodeInfo(val addresses: List<NetworkHostAndPort>,
|
||||
/** Returns true if [party] is one of the identities of this node, else false. */
|
||||
fun isLegalIdentity(party: Party): Boolean = party in legalIdentities
|
||||
|
||||
fun identityFromX500Name(name: CordaX500Name): Party {
|
||||
val identity = legalIdentitiesAndCerts.singleOrNull { it.name == name } ?: throw IllegalArgumentException("Node does not have an identity \"$name\"")
|
||||
return identity.party
|
||||
/**
|
||||
* Get a legal identity of this node from the X.500 name. This is intended for use in cases where the node is
|
||||
* expected to have a matching identity, and will throw an exception if no match is found.
|
||||
*
|
||||
* @throws IllegalArgumentException if the node has no matching identity.
|
||||
*/
|
||||
fun identityFromX500Name(name: CordaX500Name): Party = identityAndCertFromX500Name(name).party
|
||||
|
||||
/**
|
||||
* Get a legal identity and certificate of this node from the X.500 name. This is intended for use in cases where
|
||||
* the node is expected to have a matching identity, and will throw an exception if no match is found.
|
||||
*
|
||||
* @throws IllegalArgumentException if the node has no matching identity.
|
||||
*/
|
||||
fun identityAndCertFromX500Name(name: CordaX500Name): PartyAndCertificate {
|
||||
return legalIdentitiesAndCerts.singleOrNull { it.name == name } ?: throw IllegalArgumentException("Node does not have an identity \"$name\"")
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,13 @@ import net.corda.core.crypto.SignableData
|
||||
import net.corda.core.crypto.SignatureMetadata
|
||||
import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.flows.ContractUpgradeFlow
|
||||
import net.corda.core.messaging.NodeState
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.transactions.FilteredTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import rx.Observable
|
||||
import java.security.PublicKey
|
||||
import java.sql.Connection
|
||||
import java.time.Clock
|
||||
@ -29,6 +31,16 @@ interface StateLoader {
|
||||
*/
|
||||
@Throws(TransactionResolutionException::class)
|
||||
fun loadState(stateRef: StateRef): TransactionState<*>
|
||||
|
||||
/**
|
||||
* Given a [Set] of [StateRef]'s loads the referenced transaction and looks up the specified output [ContractState].
|
||||
*
|
||||
* @throws TransactionResolutionException if [stateRef] points to a non-existent transaction.
|
||||
*/
|
||||
// TODO: future implementation to use a Vault state ref -> contract state BLOB table and perform single query bulk load
|
||||
// as the existing transaction store will become encrypted at some point
|
||||
@Throws(TransactionResolutionException::class)
|
||||
fun loadStates(stateRefs: Set<StateRef>): Set<StateAndRef<ContractState>>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,6 +150,9 @@ interface ServiceHub : ServicesForResolution {
|
||||
/** The [NodeInfo] object corresponding to our own entry in the network map. */
|
||||
val myInfo: NodeInfo
|
||||
|
||||
/** The [Observable] object used to communicate to RPC clients the state of the node. */
|
||||
val myNodeStateObservable: Observable<NodeState>
|
||||
|
||||
/**
|
||||
* Return the singleton instance of the given Corda service type. This is a class that is annotated with
|
||||
* [CordaService] and will have automatically been registered by the node.
|
||||
|
@ -3,6 +3,8 @@ package net.corda.core.node.services
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
@ -33,5 +35,23 @@ interface AttachmentStorage {
|
||||
*/
|
||||
@Throws(FileAlreadyExistsException::class, IOException::class)
|
||||
fun importAttachment(jar: InputStream): AttachmentId
|
||||
|
||||
/**
|
||||
* Inserts the given attachment with additional metadata, see [importAttachment] for input stream handling
|
||||
* Extra parameters:
|
||||
* @param uploader Uploader name
|
||||
* @param filename Name of the file
|
||||
*/
|
||||
@Throws(FileAlreadyExistsException::class, IOException::class)
|
||||
fun importAttachment(jar: InputStream, uploader: String, filename: String): AttachmentId
|
||||
|
||||
/**
|
||||
* Searches attachment using given criteria and optional sort rules
|
||||
* @param criteria Query criteria to use as a filter
|
||||
* @param sorting Sorting definition, if not given, order is undefined
|
||||
*
|
||||
* @return List of AttachmentId of attachment matching criteria, sorted according to given sorting parameter
|
||||
*/
|
||||
fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort? = null): List<AttachmentId>
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package net.corda.core.node.services
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
@ -52,6 +53,7 @@ interface NetworkMapCacheBase {
|
||||
*
|
||||
* Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced.
|
||||
*/
|
||||
// TODO this list will be taken from NetworkParameters distributed by NetworkMap.
|
||||
val notaryIdentities: List<Party>
|
||||
// DOCEND 1
|
||||
|
||||
@ -115,7 +117,7 @@ interface NetworkMapCacheBase {
|
||||
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
|
||||
// DOCEND 2
|
||||
|
||||
/** Returns true if and only if the given [Party] is a notary, which is defined by the network parameters. */
|
||||
/** Checks whether a given party is an advertised notary identity. */
|
||||
fun isNotary(party: Party): Boolean = party in notaryIdentities
|
||||
|
||||
/**
|
||||
|
@ -15,13 +15,38 @@ import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.persistence.criteria.Predicate
|
||||
|
||||
interface GenericQueryCriteria<Q : GenericQueryCriteria<Q, *>, in P : BaseQueryCriteriaParser<Q, *, *>> {
|
||||
fun visit(parser: P): Collection<Predicate>
|
||||
|
||||
interface ChainableQueryCriteria<Q : GenericQueryCriteria<Q, P>, in P : BaseQueryCriteriaParser<Q, P, *>> {
|
||||
|
||||
interface AndVisitor<Q : GenericQueryCriteria<Q, P>, in P : BaseQueryCriteriaParser<Q, P, S>, in S : BaseSort> : GenericQueryCriteria<Q,P> {
|
||||
val a:Q
|
||||
val b:Q
|
||||
override fun visit(parser: P): Collection<Predicate> {
|
||||
return parser.parseAnd(this.a, this.b)
|
||||
}
|
||||
}
|
||||
|
||||
interface OrVisitor<Q : GenericQueryCriteria<Q, P>, in P : BaseQueryCriteriaParser<Q, P, S>, in S : BaseSort> : GenericQueryCriteria<Q,P> {
|
||||
val a:Q
|
||||
val b:Q
|
||||
override fun visit(parser: P): Collection<Predicate> {
|
||||
return parser.parseOr(this.a, this.b)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun and(criteria: Q): Q
|
||||
infix fun or(criteria: Q): Q
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indexing assumptions:
|
||||
* QueryCriteria assumes underlying schema tables are correctly indexed for performance.
|
||||
*/
|
||||
@CordaSerializable
|
||||
sealed class QueryCriteria {
|
||||
abstract fun visit(parser: IQueryCriteriaParser): Collection<Predicate>
|
||||
sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaParser>, GenericQueryCriteria.ChainableQueryCriteria<QueryCriteria, IQueryCriteriaParser> {
|
||||
|
||||
@CordaSerializable
|
||||
data class TimeCondition(val type: TimeInstantType, val predicate: ColumnPredicate<Instant>)
|
||||
@ -121,19 +146,6 @@ sealed class QueryCriteria {
|
||||
}
|
||||
}
|
||||
|
||||
// enable composition of [QueryCriteria]
|
||||
private data class AndComposition(val a: QueryCriteria, val b: QueryCriteria) : QueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseAnd(this.a, this.b)
|
||||
}
|
||||
}
|
||||
|
||||
private data class OrComposition(val a: QueryCriteria, val b: QueryCriteria) : QueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseOr(this.a, this.b)
|
||||
}
|
||||
}
|
||||
|
||||
// timestamps stored in the vault states table [VaultSchema.VaultStates]
|
||||
@CordaSerializable
|
||||
enum class TimeInstantType {
|
||||
@ -141,18 +153,47 @@ sealed class QueryCriteria {
|
||||
CONSUMED
|
||||
}
|
||||
|
||||
infix fun and(criteria: QueryCriteria): QueryCriteria = AndComposition(this, criteria)
|
||||
infix fun or(criteria: QueryCriteria): QueryCriteria = OrComposition(this, criteria)
|
||||
class AndComposition(override val a: QueryCriteria, override val b: QueryCriteria): QueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.AndVisitor<QueryCriteria, IQueryCriteriaParser, Sort>
|
||||
class OrComposition(override val a: QueryCriteria, override val b: QueryCriteria): QueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.OrVisitor<QueryCriteria, IQueryCriteriaParser, Sort>
|
||||
|
||||
override fun and(criteria: QueryCriteria): QueryCriteria = AndComposition(this, criteria)
|
||||
override fun or(criteria: QueryCriteria): QueryCriteria = OrComposition(this, criteria)
|
||||
}
|
||||
@CordaSerializable
|
||||
sealed class AttachmentQueryCriteria : GenericQueryCriteria<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser>, GenericQueryCriteria.ChainableQueryCriteria<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser> {
|
||||
/**
|
||||
* AttachmentsQueryCriteria:
|
||||
*/
|
||||
data class AttachmentsQueryCriteria @JvmOverloads constructor (val uploaderCondition: ColumnPredicate<String>? = null,
|
||||
val filenameCondition: ColumnPredicate<String>? = null,
|
||||
val uploadDateCondition: ColumnPredicate<Instant>? = null) : AttachmentQueryCriteria() {
|
||||
override fun visit(parser: AttachmentsQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseCriteria(this)
|
||||
}
|
||||
}
|
||||
|
||||
class AndComposition(override val a: AttachmentQueryCriteria, override val b: AttachmentQueryCriteria): AttachmentQueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.AndVisitor<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser, AttachmentSort>
|
||||
class OrComposition(override val a: AttachmentQueryCriteria, override val b: AttachmentQueryCriteria): AttachmentQueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.OrVisitor<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser, AttachmentSort>
|
||||
|
||||
override fun and(criteria: AttachmentQueryCriteria): AttachmentQueryCriteria = AndComposition(this, criteria)
|
||||
override fun or(criteria: AttachmentQueryCriteria): AttachmentQueryCriteria = OrComposition(this, criteria)
|
||||
}
|
||||
|
||||
interface BaseQueryCriteriaParser<Q: GenericQueryCriteria<Q, P>, in P: BaseQueryCriteriaParser<Q,P,S>, in S : BaseSort> {
|
||||
fun parseOr(left: Q, right: Q): Collection<Predicate>
|
||||
fun parseAnd(left: Q, right: Q): Collection<Predicate>
|
||||
fun parse(criteria: Q, sorting: S? = null): Collection<Predicate>
|
||||
}
|
||||
|
||||
@DoNotImplement
|
||||
interface IQueryCriteriaParser {
|
||||
interface IQueryCriteriaParser : BaseQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort> {
|
||||
fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.LinearStateQueryCriteria): Collection<Predicate>
|
||||
fun <L : PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.VaultQueryCriteria): Collection<Predicate>
|
||||
fun parseOr(left: QueryCriteria, right: QueryCriteria): Collection<Predicate>
|
||||
fun parseAnd(left: QueryCriteria, right: QueryCriteria): Collection<Predicate>
|
||||
fun parse(criteria: QueryCriteria, sorting: Sort? = null): Collection<Predicate>
|
||||
}
|
||||
|
||||
interface AttachmentsQueryCriteriaParser : BaseQueryCriteriaParser<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser, AttachmentSort>{
|
||||
fun parseCriteria(criteria: AttachmentQueryCriteria.AttachmentsQueryCriteria): Collection<Predicate>
|
||||
}
|
||||
|
@ -127,12 +127,14 @@ data class PageSpecification(val pageNumber: Int = -1, val pageSize: Int = DEFAU
|
||||
val isDefault = (pageSize == DEFAULT_PAGE_SIZE && pageNumber == -1)
|
||||
}
|
||||
|
||||
abstract class BaseSort
|
||||
|
||||
/**
|
||||
* Sort allows specification of a set of entity attribute names and their associated directionality
|
||||
* and null handling, to be applied upon processing a query specification.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Sort(val columns: Collection<SortColumn>) {
|
||||
data class Sort(val columns: Collection<SortColumn>) : BaseSort() {
|
||||
@CordaSerializable
|
||||
enum class Direction {
|
||||
ASC,
|
||||
@ -177,6 +179,21 @@ data class Sort(val columns: Collection<SortColumn>) {
|
||||
val direction: Sort.Direction = Sort.Direction.ASC)
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
data class AttachmentSort(val columns: Collection<AttachmentSortColumn>) : BaseSort() {
|
||||
|
||||
enum class AttachmentSortAttribute(val columnName: String) {
|
||||
INSERTION_DATE("insertion_date"),
|
||||
UPLOADER("uploader"),
|
||||
FILENAME("filename")
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
data class AttachmentSortColumn(
|
||||
val sortAttribute: AttachmentSortAttribute,
|
||||
val direction: Sort.Direction = Sort.Direction.ASC)
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
sealed class SortAttribute {
|
||||
/**
|
||||
@ -257,6 +274,11 @@ object Builder {
|
||||
fun <R : Comparable<R>> between(from: R, to: R) = ColumnPredicate.Between(from, to)
|
||||
fun <R : Comparable<R>> `in`(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection)
|
||||
fun <R : Comparable<R>> notIn(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection)
|
||||
fun like(string: String) = ColumnPredicate.Likeness(LikenessOperator.LIKE, string)
|
||||
fun notLike(string: String) = ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string)
|
||||
fun <R> isNull() = ColumnPredicate.NullExpression<R>(NullOperator.IS_NULL)
|
||||
fun <R> isNotNull() = ColumnPredicate.NullExpression<R>(NullOperator.NOT_NULL)
|
||||
|
||||
|
||||
fun <O> KProperty1<O, String?>.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
|
||||
@JvmStatic
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.corda.core.schemas
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.FungibleAsset
|
||||
import net.corda.core.contracts.OwnableState
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import org.hibernate.annotations.Type
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.ElementCollection
|
||||
import javax.persistence.MappedSuperclass
|
||||
|
||||
/**
|
||||
* JPA representation of the common schema entities
|
||||
@ -74,7 +74,8 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers
|
||||
@Column(name = "issuer_name")
|
||||
var issuer: AbstractParty,
|
||||
|
||||
@Column(name = "issuer_reference")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
@ -2,8 +2,7 @@ package net.corda.core.serialization
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.internal.WriteOnceProperty
|
||||
import net.corda.core.serialization.internal.SerializationEnvironment
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.sequence
|
||||
@ -53,7 +52,7 @@ abstract class SerializationFactory {
|
||||
* A context to use as a default if you do not require a specially configured context. It will be the current context
|
||||
* if the use is somehow nested (see [currentContext]).
|
||||
*/
|
||||
val defaultContext: SerializationContext get() = currentContext ?: SerializationDefaults.P2P_CONTEXT
|
||||
val defaultContext: SerializationContext get() = currentContext ?: effectiveSerializationEnv.p2pContext
|
||||
|
||||
private val _currentContext = ThreadLocal<SerializationContext?>()
|
||||
|
||||
@ -90,7 +89,7 @@ abstract class SerializationFactory {
|
||||
/**
|
||||
* A default factory for serialization/deserialization, taking into account the [currentFactory] if set.
|
||||
*/
|
||||
val defaultFactory: SerializationFactory get() = currentFactory ?: SerializationDefaults.SERIALIZATION_FACTORY
|
||||
val defaultFactory: SerializationFactory get() = currentFactory ?: effectiveSerializationEnv.serializationFactory
|
||||
|
||||
/**
|
||||
* If there is a need to nest serialization/deserialization with a modified context during serialization or deserialization,
|
||||
@ -173,13 +172,13 @@ interface SerializationContext {
|
||||
/**
|
||||
* Global singletons to be used as defaults that are injected elsewhere (generally, in the node or in RPC client).
|
||||
*/
|
||||
object SerializationDefaults : SerializationEnvironment {
|
||||
override var SERIALIZATION_FACTORY: SerializationFactory by WriteOnceProperty()
|
||||
override var P2P_CONTEXT: SerializationContext by WriteOnceProperty()
|
||||
override var RPC_SERVER_CONTEXT: SerializationContext by WriteOnceProperty()
|
||||
override var RPC_CLIENT_CONTEXT: SerializationContext by WriteOnceProperty()
|
||||
override var STORAGE_CONTEXT: SerializationContext by WriteOnceProperty()
|
||||
override var CHECKPOINT_CONTEXT: SerializationContext by WriteOnceProperty()
|
||||
object SerializationDefaults {
|
||||
val SERIALIZATION_FACTORY get() = effectiveSerializationEnv.serializationFactory
|
||||
val P2P_CONTEXT get() = effectiveSerializationEnv.p2pContext
|
||||
val RPC_SERVER_CONTEXT get() = effectiveSerializationEnv.rpcServerContext
|
||||
val RPC_CLIENT_CONTEXT get() = effectiveSerializationEnv.rpcClientContext
|
||||
val STORAGE_CONTEXT get() = effectiveSerializationEnv.storageContext
|
||||
val CHECKPOINT_CONTEXT get() = effectiveSerializationEnv.checkpointContext
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,13 +1,61 @@
|
||||
package net.corda.core.serialization.internal
|
||||
|
||||
import net.corda.core.internal.InheritableThreadLocalToggleField
|
||||
import net.corda.core.internal.SimpleToggleField
|
||||
import net.corda.core.internal.ThreadLocalToggleField
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
|
||||
interface SerializationEnvironment {
|
||||
val SERIALIZATION_FACTORY: SerializationFactory
|
||||
val P2P_CONTEXT: SerializationContext
|
||||
val RPC_SERVER_CONTEXT: SerializationContext
|
||||
val RPC_CLIENT_CONTEXT: SerializationContext
|
||||
val STORAGE_CONTEXT: SerializationContext
|
||||
val CHECKPOINT_CONTEXT: SerializationContext
|
||||
val serializationFactory: SerializationFactory
|
||||
val p2pContext: SerializationContext
|
||||
val rpcServerContext: SerializationContext
|
||||
val rpcClientContext: SerializationContext
|
||||
val storageContext: SerializationContext
|
||||
val checkpointContext: SerializationContext
|
||||
}
|
||||
|
||||
class SerializationEnvironmentImpl(
|
||||
override val serializationFactory: SerializationFactory,
|
||||
override val p2pContext: SerializationContext,
|
||||
rpcServerContext: SerializationContext? = null,
|
||||
rpcClientContext: SerializationContext? = null,
|
||||
storageContext: SerializationContext? = null,
|
||||
checkpointContext: SerializationContext? = null) : SerializationEnvironment {
|
||||
// Those that are passed in as null are never inited:
|
||||
override lateinit var rpcServerContext: SerializationContext
|
||||
override lateinit var rpcClientContext: SerializationContext
|
||||
override lateinit var storageContext: SerializationContext
|
||||
override lateinit var checkpointContext: SerializationContext
|
||||
|
||||
init {
|
||||
rpcServerContext?.let { this.rpcServerContext = it }
|
||||
rpcClientContext?.let { this.rpcClientContext = it }
|
||||
storageContext?.let { this.storageContext = it }
|
||||
checkpointContext?.let { this.checkpointContext = it }
|
||||
}
|
||||
}
|
||||
|
||||
private val _nodeSerializationEnv = SimpleToggleField<SerializationEnvironment>("nodeSerializationEnv", true)
|
||||
@VisibleForTesting
|
||||
val _globalSerializationEnv = SimpleToggleField<SerializationEnvironment>("globalSerializationEnv")
|
||||
@VisibleForTesting
|
||||
val _contextSerializationEnv = ThreadLocalToggleField<SerializationEnvironment>("contextSerializationEnv")
|
||||
@VisibleForTesting
|
||||
val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField<SerializationEnvironment>("inheritableContextSerializationEnv") { stack ->
|
||||
stack.fold(false) { isAGlobalThreadBeingCreated, e ->
|
||||
isAGlobalThreadBeingCreated ||
|
||||
(e.className == "io.netty.util.concurrent.GlobalEventExecutor" && e.methodName == "startThread") ||
|
||||
(e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" && e.methodName == "newThread")
|
||||
}
|
||||
}
|
||||
private val serializationEnvProperties = listOf(_nodeSerializationEnv, _globalSerializationEnv, _contextSerializationEnv, _inheritableContextSerializationEnv)
|
||||
val effectiveSerializationEnv: SerializationEnvironment
|
||||
get() = serializationEnvProperties.map { Pair(it, it.get()) }.filter { it.second != null }.run {
|
||||
singleOrNull()?.run {
|
||||
second!!
|
||||
} ?: throw IllegalStateException("Expected exactly 1 of {${serializationEnvProperties.joinToString(", ") { it.name }}} but got: {${joinToString(", ") { it.first.name }}}")
|
||||
}
|
||||
/** Should be set once in main. */
|
||||
var nodeSerializationEnv by _nodeSerializationEnv
|
||||
|
@ -9,10 +9,16 @@ import net.corda.core.utilities.toNonEmptySet
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
import java.util.*
|
||||
|
||||
/** An interface for transactions containing signatures, with logic for signature verification */
|
||||
@DoNotImplement
|
||||
interface TransactionWithSignatures : NamedByHash {
|
||||
/**
|
||||
* List of signatures on this transaction.
|
||||
* @see checkSignaturesAreValid
|
||||
* @see verifyRequiredSignatures
|
||||
*/
|
||||
val sigs: List<TransactionSignature>
|
||||
|
||||
/** Specifies all the public keys that require signatures for the transaction to be valid */
|
||||
@ -26,7 +32,7 @@ interface TransactionWithSignatures : NamedByHash {
|
||||
* @throws SignaturesMissingException if any signatures should have been present but were not.
|
||||
*/
|
||||
@Throws(SignatureException::class)
|
||||
fun verifyRequiredSignatures() = verifySignaturesExcept()
|
||||
fun verifyRequiredSignatures() = verifySignaturesExcept(emptySet())
|
||||
|
||||
/**
|
||||
* Verifies the signatures on this transaction and throws if any are missing which aren't passed as parameters.
|
||||
@ -42,6 +48,23 @@ interface TransactionWithSignatures : NamedByHash {
|
||||
*/
|
||||
@Throws(SignatureException::class)
|
||||
fun verifySignaturesExcept(vararg allowedToBeMissing: PublicKey) {
|
||||
verifySignaturesExcept(Arrays.asList(*allowedToBeMissing))
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the signatures on this transaction and throws if any are missing which aren't passed as parameters.
|
||||
* In this context, "verifying" means checking they are valid signatures and that their public keys are in
|
||||
* the [requiredSigningKeys] set.
|
||||
*
|
||||
* Normally you would not provide any keys to this function, but if you're in the process of building a partial
|
||||
* transaction and you want to access the contents before you've signed it, you can specify your own keys here
|
||||
* to bypass that check.
|
||||
*
|
||||
* @throws SignatureException if any signatures are invalid or unrecognised.
|
||||
* @throws SignaturesMissingException if any signatures should have been present but were not.
|
||||
*/
|
||||
@Throws(SignatureException::class)
|
||||
fun verifySignaturesExcept(allowedToBeMissing: Collection<PublicKey>) {
|
||||
checkSignaturesAreValid()
|
||||
|
||||
val needed = getMissingSigners() - allowedToBeMissing
|
||||
@ -53,7 +76,7 @@ interface TransactionWithSignatures : NamedByHash {
|
||||
* Mathematically validates the signatures that are present on this transaction. This does not imply that
|
||||
* the signatures are by the right keys, or that there are sufficient signatures, just that they aren't
|
||||
* corrupt. If you use this function directly you'll need to do the other checks yourself. Probably you
|
||||
* want [verifySignatures] instead.
|
||||
* want [verifyRequiredSignatures] instead.
|
||||
*
|
||||
* @throws InvalidKeyException if the key on a signature is invalid.
|
||||
* @throws SignatureException if a signature fails to verify.
|
||||
@ -80,7 +103,6 @@ interface TransactionWithSignatures : NamedByHash {
|
||||
val sigKeys = sigs.map { it.by }.toSet()
|
||||
// TODO Problem is that we can get single PublicKey wrapped as CompositeKey in allowedToBeMissing/mustSign
|
||||
// equals on CompositeKey won't catch this case (do we want to single PublicKey be equal to the same key wrapped in CompositeKey with threshold 1?)
|
||||
val missing = requiredSigningKeys.filter { !it.isFulfilledBy(sigKeys) }.toSet()
|
||||
return missing
|
||||
return requiredSigningKeys.filter { !it.isFulfilledBy(sigKeys) }.toSet()
|
||||
}
|
||||
}
|
46
core/src/main/kotlin/net/corda/core/utilities/Id.kt
Normal file
46
core/src/main/kotlin/net/corda/core/utilities/Id.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package net.corda.core.utilities
|
||||
|
||||
import java.time.Instant
|
||||
import java.time.Instant.now
|
||||
|
||||
/**
|
||||
* Represents a unique, timestamped id.
|
||||
* @param value unique value of the id.
|
||||
* @param entityType optional id entity type.
|
||||
* @param timestamp timestamp for the id.
|
||||
*/
|
||||
open class Id<out VALUE : Any>(val value: VALUE, val entityType: String?, val timestamp: Instant) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates an id using [Instant.now] as timestamp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <V : Any> newInstance(value: V, entityType: String? = null, timestamp: Instant = now()) = Id(value, entityType, timestamp)
|
||||
}
|
||||
|
||||
final override fun equals(other: Any?): Boolean {
|
||||
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Id<*>
|
||||
|
||||
if (value != other.value) return false
|
||||
if (entityType != other.entityType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
final override fun hashCode(): Int {
|
||||
|
||||
var result = value.hashCode()
|
||||
result = 31 * result + (entityType?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
final override fun toString(): String {
|
||||
|
||||
return "$value, timestamp: $timestamp" + (entityType?.let { ", entityType: $it" } ?: "")
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package net.corda.core.utilities
|
||||
|
||||
import java.util.*
|
||||
|
||||
class UuidGenerator {
|
||||
|
||||
companion object {
|
||||
fun next() : UUID = UUID.randomUUID()
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ import co.paralleluniverse.fibers.Suspendable;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.node.internal.StartedNode;
|
||||
import net.corda.testing.node.MockNetwork;
|
||||
import net.corda.testing.TestConstants;
|
||||
import net.corda.testing.node.MockNetwork;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -13,22 +13,22 @@ import org.junit.Test;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static net.corda.testing.CoreTestUtils.chooseIdentity;
|
||||
import static net.corda.testing.CoreTestUtils.singleIdentity;
|
||||
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();
|
||||
private StartedNode<MockNetwork.MockNode> aliceNode;
|
||||
private StartedNode<MockNetwork.MockNode> bobNode;
|
||||
private Party bob;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
|
||||
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
|
||||
mockNet.runNetwork();
|
||||
// Ensure registration was successful
|
||||
aliceNode.getInternals().getNodeReadyFuture().get();
|
||||
bob = singleIdentity(bobNode.getInfo());
|
||||
}
|
||||
|
||||
@After
|
||||
@ -39,7 +39,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(bob)).getResultFuture();
|
||||
mockNet.runNetwork();
|
||||
assertThat(result.get()).isEqualTo("Hello");
|
||||
}
|
||||
@ -54,8 +54,8 @@ 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();
|
||||
PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(bob, receiveType);
|
||||
Future<?> result = startFlow(aliceNode.getServices(), flow).getResultFuture();
|
||||
mockNet.runNetwork();
|
||||
try {
|
||||
result.get();
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.corda.core.flows;
|
||||
|
||||
import net.corda.core.serialization.SerializationDefaults;
|
||||
import net.corda.core.serialization.SerializationFactory;
|
||||
import net.corda.testing.SerializationEnvironmentRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static net.corda.core.serialization.SerializationAPIKt.serialize;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Enforce parts of the serialization API that aren't obvious from looking at the {@link net.corda.core.serialization.SerializationAPIKt} code.
|
||||
*/
|
||||
public class SerializationApiInJavaTest {
|
||||
@Rule
|
||||
public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule();
|
||||
|
||||
@Test
|
||||
public void enforceSerializationFactoryApi() {
|
||||
assertNull(SerializationFactory.Companion.getCurrentFactory());
|
||||
SerializationFactory factory = SerializationFactory.Companion.getDefaultFactory();
|
||||
assertNull(factory.getCurrentContext());
|
||||
serialize("hello", factory, factory.getDefaultContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enforceSerializationDefaultsApi() {
|
||||
SerializationDefaults defaults = SerializationDefaults.INSTANCE;
|
||||
SerializationFactory factory = defaults.getSERIALIZATION_FACTORY();
|
||||
serialize("hello", factory, defaults.getP2P_CONTEXT());
|
||||
serialize("hello", factory, defaults.getRPC_SERVER_CONTEXT());
|
||||
serialize("hello", factory, defaults.getRPC_CLIENT_CONTEXT());
|
||||
serialize("hello", factory, defaults.getSTORAGE_CONTEXT());
|
||||
serialize("hello", factory, defaults.getCHECKPOINT_CONTEXT());
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.testing.*
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.security.PublicKey
|
||||
@ -23,44 +24,46 @@ class PartialMerkleTreeTest {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val nodes = "abcdef"
|
||||
private val hashed = nodes.map { node ->
|
||||
withTestSerialization {
|
||||
node.serialize().sha256()
|
||||
}
|
||||
}
|
||||
private val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash
|
||||
private val merkleTree = MerkleTree.getMerkleTree(hashed)
|
||||
|
||||
private val testLedger = ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash") {
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
private lateinit var hashed: List<SecureHash.SHA256>
|
||||
private lateinit var expectedRoot: SecureHash
|
||||
private lateinit var merkleTree: MerkleTree
|
||||
private lateinit var testLedger: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
|
||||
private lateinit var txs: List<WireTransaction>
|
||||
private lateinit var testTx: WireTransaction
|
||||
@Before
|
||||
fun init() {
|
||||
hashed = nodes.map { it.serialize().sha256() }
|
||||
expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash
|
||||
merkleTree = MerkleTree.getMerkleTree(hashed)
|
||||
testLedger = ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash") {
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
}
|
||||
output(Cash.PROGRAM_ID, "dummy cash 1") {
|
||||
Cash.State(
|
||||
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MINI_CORP
|
||||
)
|
||||
}
|
||||
}
|
||||
output(Cash.PROGRAM_ID, "dummy cash 1") {
|
||||
Cash.State(
|
||||
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MINI_CORP
|
||||
)
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input("MEGA_CORP cash")
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input("MEGA_CORP cash")
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
txs = testLedger.interpreter.transactionsToVerify
|
||||
testTx = txs[0]
|
||||
}
|
||||
|
||||
private val txs = testLedger.interpreter.transactionsToVerify
|
||||
private val testTx = txs[0]
|
||||
|
||||
// Building full Merkle Tree tests.
|
||||
@Test
|
||||
fun `building Merkle tree with 6 nodes - no rightmost nodes`() {
|
||||
|
@ -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
|
||||
@ -54,8 +51,6 @@ class AttachmentTests {
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
|
||||
// Ensure that registration was successful before progressing any further
|
||||
mockNet.runNetwork()
|
||||
val alice = aliceNode.info.singleIdentity()
|
||||
|
||||
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
||||
@ -91,15 +86,11 @@ class AttachmentTests {
|
||||
val aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(BOB.name)
|
||||
|
||||
// Ensure that registration was successful before progressing any further
|
||||
mockNet.runNetwork()
|
||||
|
||||
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
||||
bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
||||
|
||||
// Get node one to fetch a non-existent attachment.
|
||||
val hash = SecureHash.randomSHA256()
|
||||
mockNet.runNetwork()
|
||||
val alice = aliceNode.info.singleIdentity()
|
||||
val bobFlow = bobNode.startAttachmentFlow(setOf(hash), alice)
|
||||
mockNet.runNetwork()
|
||||
@ -116,7 +107,6 @@ class AttachmentTests {
|
||||
}
|
||||
})
|
||||
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
|
||||
mockNet.runNetwork()
|
||||
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)
|
||||
|
||||
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
|
||||
|
@ -10,7 +10,6 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -23,10 +22,6 @@ import kotlin.reflect.KClass
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class CollectSignaturesFlowTests {
|
||||
companion object {
|
||||
private val cordappPackages = listOf("net.corda.testing.contracts")
|
||||
}
|
||||
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||
@ -38,11 +33,10 @@ class CollectSignaturesFlowTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = cordappPackages)
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
|
||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
bobNode = mockNet.createPartyNode(BOB.name)
|
||||
charlieNode = mockNet.createPartyNode(CHARLIE.name)
|
||||
mockNet.runNetwork()
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
charlie = charlieNode.info.singleIdentity()
|
||||
@ -60,60 +54,14 @@ class CollectSignaturesFlowTests {
|
||||
}
|
||||
}
|
||||
|
||||
// With this flow, the initiators sends an "offer" to the responder, who then initiates the collect signatures flow.
|
||||
// This flow is a more simplified version of the "TwoPartyTrade" flow and is a useful example of how both the
|
||||
// "collectSignaturesFlow" and "SignTransactionFlow" can be used in practise.
|
||||
object TestFlow {
|
||||
@InitiatingFlow
|
||||
class Initiator(private val state: DummyContract.MultiOwnerState, private val otherParty: Party) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val session = initiateFlow(otherParty)
|
||||
session.send(state)
|
||||
|
||||
val flow = object : SignTransactionFlow(session) {
|
||||
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||
val tx = stx.tx
|
||||
val ltx = tx.toLedgerTransaction(serviceHub)
|
||||
"There should only be one output state" using (tx.outputs.size == 1)
|
||||
"There should only be one output state" using (tx.inputs.isEmpty())
|
||||
val magicNumberState = ltx.outputsOfType<DummyContract.MultiOwnerState>().single()
|
||||
"Must be 1337 or greater" using (magicNumberState.magicNumber >= 1337)
|
||||
}
|
||||
}
|
||||
|
||||
val stx = subFlow(flow)
|
||||
return waitForLedgerCommit(stx.id)
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatedBy(TestFlow.Initiator::class)
|
||||
class Responder(private val initiatingSession: FlowSession) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val state = initiatingSession.receive<DummyContract.MultiOwnerState>().unwrap { it }
|
||||
val notary = serviceHub.getDefaultNotary()
|
||||
|
||||
val myInputKeys = state.participants.map { it.owningKey }
|
||||
val command = Command(DummyContract.Commands.Create(), myInputKeys)
|
||||
val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DummyContract.PROGRAM_ID), command)
|
||||
val ptx = serviceHub.signInitialTransaction(builder)
|
||||
val signature = subFlow(CollectSignatureFlow(ptx, initiatingSession, initiatingSession.counterparty.owningKey))
|
||||
val stx = ptx + signature
|
||||
return subFlow(FinalityFlow(stx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With this flow, the initiator starts the "CollectTransactionFlow". It is then the responders responsibility to
|
||||
// override "checkTransaction" and add whatever logic their require to verify the SignedTransaction they are
|
||||
// receiving off the wire.
|
||||
object TestFlowTwo {
|
||||
object TestFlow {
|
||||
@InitiatingFlow
|
||||
class Initiator(private val state: DummyContract.MultiOwnerState) : FlowLogic<SignedTransaction>() {
|
||||
class Initiator(private val state: DummyContract.MultiOwnerState, private val notary: Party) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val notary = serviceHub.getDefaultNotary()
|
||||
val myInputKeys = state.participants.map { it.owningKey }
|
||||
val command = Command(DummyContract.Commands.Create(), myInputKeys)
|
||||
val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DummyContract.PROGRAM_ID), command)
|
||||
@ -124,7 +72,7 @@ class CollectSignaturesFlowTests {
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatedBy(TestFlowTwo.Initiator::class)
|
||||
@InitiatedBy(TestFlow.Initiator::class)
|
||||
class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable override fun call() {
|
||||
val signFlow = object : SignTransactionFlow(otherSideSession) {
|
||||
@ -154,11 +102,11 @@ class CollectSignaturesFlowTests {
|
||||
// Normally this is handled by TransactionKeyFlow, but here we have to manually let A know about the identity
|
||||
aliceNode.services.identityService.verifyAndRegisterIdentity(bConfidentialIdentity)
|
||||
}
|
||||
registerFlowOnAllNodes(TestFlowTwo.Responder::class)
|
||||
registerFlowOnAllNodes(TestFlow.Responder::class)
|
||||
val magicNumber = 1337
|
||||
val parties = listOf(alice, bConfidentialIdentity.party, charlie)
|
||||
val state = DummyContract.MultiOwnerState(magicNumber, parties)
|
||||
val flow = aliceNode.services.startFlow(TestFlowTwo.Initiator(state))
|
||||
val flow = aliceNode.services.startFlow(TestFlow.Initiator(state, notary))
|
||||
mockNet.runNetwork()
|
||||
val result = flow.resultFuture.getOrThrow()
|
||||
result.verifyRequiredSignatures()
|
||||
@ -181,7 +129,7 @@ class CollectSignaturesFlowTests {
|
||||
@Test
|
||||
fun `fails when not signed by initiator`() {
|
||||
val onePartyDummyContract = DummyContract.generateInitial(1337, notary, alice.ref(1))
|
||||
val miniCorpServices = MockServices(cordappPackages, MINI_CORP_KEY)
|
||||
val miniCorpServices = MockServices(listOf("net.corda.testing.contracts"), MINI_CORP.name, MINI_CORP_KEY)
|
||||
val ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract)
|
||||
val flow = aliceNode.services.startFlow(CollectSignaturesFlow(ptx, emptySet()))
|
||||
mockNet.runNetwork()
|
||||
|
@ -23,6 +23,10 @@ import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyContractV2
|
||||
import net.corda.testing.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcDriver
|
||||
import net.corda.testing.internal.rpcTestUser
|
||||
import net.corda.testing.internal.startRpcClient
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -33,21 +37,24 @@ import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class ContractUpgradeFlowTest {
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||
lateinit var notary: Party
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var notary: Party
|
||||
private lateinit var alice: Party
|
||||
private lateinit var bob: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
|
||||
aliceNode = mockNet.createPartyNode(ALICE.name)
|
||||
bobNode = mockNet.createPartyNode(BOB.name)
|
||||
aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
|
||||
// Process registration
|
||||
mockNet.runNetwork()
|
||||
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
@ -58,11 +65,11 @@ class ContractUpgradeFlowTest {
|
||||
@Test
|
||||
fun `2 parties contract upgrade`() {
|
||||
// Create dummy contract.
|
||||
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, aliceNode.info.chooseIdentity().ref(1), bobNode.info.chooseIdentity().ref(1))
|
||||
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, alice.ref(1), bob.ref(1))
|
||||
val signedByA = aliceNode.services.signInitialTransaction(twoPartyDummyContract)
|
||||
val stx = bobNode.services.addSignature(signedByA)
|
||||
|
||||
aliceNode.services.startFlow(FinalityFlow(stx, setOf(bobNode.info.chooseIdentity())))
|
||||
aliceNode.services.startFlow(FinalityFlow(stx, setOf(bob)))
|
||||
mockNet.runNetwork()
|
||||
|
||||
val atx = aliceNode.database.transaction { aliceNode.services.validatedTransactions.getTransaction(stx.id) }
|
||||
@ -128,7 +135,7 @@ class ContractUpgradeFlowTest {
|
||||
fun `2 parties contract upgrade using RPC`() {
|
||||
rpcDriver(initialiseSerialization = false) {
|
||||
// Create dummy contract.
|
||||
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, aliceNode.info.chooseIdentity().ref(1), bobNode.info.chooseIdentity().ref(1))
|
||||
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, alice.ref(1), bob.ref(1))
|
||||
val signedByA = aliceNode.services.signInitialTransaction(twoPartyDummyContract)
|
||||
val stx = bobNode.services.addSignature(signedByA)
|
||||
|
||||
@ -140,7 +147,7 @@ class ContractUpgradeFlowTest {
|
||||
))
|
||||
val rpcA = startProxy(aliceNode, user)
|
||||
val rpcB = startProxy(bobNode, user)
|
||||
val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(bobNode.info.chooseIdentity()))
|
||||
val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(bob))
|
||||
mockNet.runNetwork()
|
||||
handle.returnValue.getOrThrow()
|
||||
|
||||
@ -202,7 +209,7 @@ class ContractUpgradeFlowTest {
|
||||
@Test
|
||||
fun `upgrade Cash to v2`() {
|
||||
// Create some cash.
|
||||
val chosenIdentity = aliceNode.info.chooseIdentity()
|
||||
val chosenIdentity = alice
|
||||
val result = aliceNode.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
val stx = result.getOrThrow().stx
|
||||
|
@ -28,12 +28,11 @@ class FinalityFlowTests {
|
||||
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
|
||||
val aliceNode = mockNet.createPartyNode(ALICE_NAME)
|
||||
val bobNode = mockNet.createPartyNode(BOB_NAME)
|
||||
mockNet.runNetwork()
|
||||
aliceServices = aliceNode.services
|
||||
bobServices = bobNode.services
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
notary = aliceServices.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -5,8 +5,9 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
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.singleIdentity
|
||||
import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
@ -19,7 +20,7 @@ class ReceiveMultipleFlowTests {
|
||||
val stringValue = "Thriller"
|
||||
nodes[2].registerAnswer(AlgorithmDefinition::class, stringValue)
|
||||
|
||||
val flow = nodes[0].services.startFlow(ParallelAlgorithmMap(nodes[1].info.chooseIdentity(), nodes[2].info.chooseIdentity()))
|
||||
val flow = nodes[0].services.startFlow(ParallelAlgorithmMap(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity()))
|
||||
runNetwork()
|
||||
|
||||
val result = flow.resultFuture.getOrThrow()
|
||||
@ -36,7 +37,7 @@ class ReceiveMultipleFlowTests {
|
||||
val value2 = 6.0
|
||||
nodes[2].registerAnswer(ParallelAlgorithmList::class, value2)
|
||||
|
||||
val flow = nodes[0].services.startFlow(ParallelAlgorithmList(nodes[1].info.chooseIdentity(), nodes[2].info.chooseIdentity()))
|
||||
val flow = nodes[0].services.startFlow(ParallelAlgorithmList(nodes[1].info.singleIdentity(), nodes[2].info.singleIdentity()))
|
||||
runNetwork()
|
||||
val data = flow.resultFuture.getOrThrow()
|
||||
|
||||
|
@ -10,10 +10,10 @@ import net.corda.core.utilities.sequence
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.MEGA_CORP
|
||||
import net.corda.testing.MINI_CORP
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.singleIdentity
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -44,10 +44,9 @@ class ResolveTransactionsFlowTest {
|
||||
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
|
||||
megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
|
||||
miniCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
|
||||
mockNet.runNetwork()
|
||||
notary = notaryNode.services.getDefaultNotary()
|
||||
megaCorp = megaCorpNode.info.chooseIdentity()
|
||||
miniCorp = miniCorpNode.info.chooseIdentity()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
megaCorp = megaCorpNode.info.singleIdentity()
|
||||
miniCorp = miniCorpNode.info.singleIdentity()
|
||||
}
|
||||
|
||||
@After
|
||||
@ -56,7 +55,6 @@ class ResolveTransactionsFlowTest {
|
||||
}
|
||||
// DOCEND 3
|
||||
|
||||
|
||||
// DOCSTART 1
|
||||
@Test
|
||||
fun `resolve from two hashes`() {
|
||||
|
210
core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt
Normal file
210
core/src/test/kotlin/net/corda/core/internal/ToggleFieldTest.kt
Normal file
@ -0,0 +1,210 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import com.nhaarman.mockito_kotlin.argThat
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.verifyNoMoreInteractions
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runners.model.Statement
|
||||
import org.slf4j.Logger
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
private fun <T> withSingleThreadExecutor(callable: ExecutorService.() -> T) = Executors.newSingleThreadExecutor().run {
|
||||
try {
|
||||
fork {}.getOrThrow() // Start the thread.
|
||||
callable()
|
||||
} finally {
|
||||
shutdown()
|
||||
while (!awaitTermination(1, TimeUnit.SECONDS)) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleFieldTest {
|
||||
companion object {
|
||||
@Suppress("JAVA_CLASS_ON_COMPANION")
|
||||
private val companionName = javaClass.name
|
||||
|
||||
private fun <T> globalThreadCreationMethod(task: () -> T) = task()
|
||||
}
|
||||
|
||||
private val log = mock<Logger>()
|
||||
@Rule
|
||||
@JvmField
|
||||
val verifyNoMoreInteractions = TestRule { base, _ ->
|
||||
object : Statement() {
|
||||
override fun evaluate() {
|
||||
base.evaluate()
|
||||
verifyNoMoreInteractions(log) // Only on success.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> inheritableThreadLocalToggleField() = InheritableThreadLocalToggleField<T>("inheritable", log) { stack ->
|
||||
stack.fold(false) { isAGlobalThreadBeingCreated, e ->
|
||||
isAGlobalThreadBeingCreated || (e.className == companionName && e.methodName == "globalThreadCreationMethod")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toggle is enforced`() {
|
||||
listOf(SimpleToggleField<String>("simple"), ThreadLocalToggleField<String>("local"), inheritableThreadLocalToggleField()).forEach { field ->
|
||||
assertNull(field.get())
|
||||
assertThatThrownBy { field.set(null) }.isInstanceOf(IllegalStateException::class.java)
|
||||
field.set("hello")
|
||||
assertEquals("hello", field.get())
|
||||
assertThatThrownBy { field.set("world") }.isInstanceOf(IllegalStateException::class.java)
|
||||
assertEquals("hello", field.get())
|
||||
assertThatThrownBy { field.set("hello") }.isInstanceOf(IllegalStateException::class.java)
|
||||
field.set(null)
|
||||
assertNull(field.get())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `write-at-most-once field works`() {
|
||||
val field = SimpleToggleField<String>("field", true)
|
||||
assertNull(field.get())
|
||||
assertThatThrownBy { field.set(null) }.isInstanceOf(IllegalStateException::class.java)
|
||||
field.set("finalValue")
|
||||
assertEquals("finalValue", field.get())
|
||||
listOf("otherValue", "finalValue", null).forEach { value ->
|
||||
assertThatThrownBy { field.set(value) }.isInstanceOf(IllegalStateException::class.java)
|
||||
assertEquals("finalValue", field.get())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `thread local works`() {
|
||||
val field = ThreadLocalToggleField<String>("field")
|
||||
assertNull(field.get())
|
||||
field.set("hello")
|
||||
assertEquals("hello", field.get())
|
||||
withSingleThreadExecutor {
|
||||
assertNull(fork(field::get).getOrThrow())
|
||||
}
|
||||
field.set(null)
|
||||
assertNull(field.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `inheritable thread local works`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
assertNull(field.get())
|
||||
field.set("hello")
|
||||
assertEquals("hello", field.get())
|
||||
withSingleThreadExecutor {
|
||||
assertEquals("hello", fork(field::get).getOrThrow())
|
||||
}
|
||||
field.set(null)
|
||||
assertNull(field.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existing threads do not inherit`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
withSingleThreadExecutor {
|
||||
field.set("hello")
|
||||
assertEquals("hello", field.get())
|
||||
assertNull(fork(field::get).getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `inherited values are poisoned on clear`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
field.set("hello")
|
||||
withSingleThreadExecutor {
|
||||
assertEquals("hello", fork(field::get).getOrThrow())
|
||||
val threadName = fork { Thread.currentThread().name }.getOrThrow()
|
||||
listOf(null, "world").forEach { value ->
|
||||
field.set(value)
|
||||
assertEquals(value, field.get())
|
||||
val future = fork(field::get)
|
||||
assertThatThrownBy { future.getOrThrow() }
|
||||
.isInstanceOf(ThreadLeakException::class.java)
|
||||
.hasMessageContaining(threadName)
|
||||
}
|
||||
}
|
||||
withSingleThreadExecutor {
|
||||
assertEquals("world", fork(field::get).getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
/** We log an error rather than failing-fast as the new thread may be an undetected global. */
|
||||
@Test
|
||||
fun `leaked thread propagates holder to non-global thread, with error`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
field.set("hello")
|
||||
withSingleThreadExecutor {
|
||||
assertEquals("hello", fork(field::get).getOrThrow())
|
||||
field.set(null) // The executor thread is now considered leaked.
|
||||
fork {
|
||||
val leakedThreadName = Thread.currentThread().name
|
||||
verifyNoMoreInteractions(log)
|
||||
withSingleThreadExecutor {
|
||||
// If ThreadLeakException is seen in practice, these errors form a trail of where the holder has been:
|
||||
verify(log).error(argThat { contains(leakedThreadName) })
|
||||
val newThreadName = fork { Thread.currentThread().name }.getOrThrow()
|
||||
val future = fork(field::get)
|
||||
assertThatThrownBy { future.getOrThrow() }
|
||||
.isInstanceOf(ThreadLeakException::class.java)
|
||||
.hasMessageContaining(newThreadName)
|
||||
fork {
|
||||
verifyNoMoreInteractions(log)
|
||||
withSingleThreadExecutor {
|
||||
verify(log).error(argThat { contains(newThreadName) })
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `leaked thread does not propagate holder to global thread, with warning`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
field.set("hello")
|
||||
withSingleThreadExecutor {
|
||||
assertEquals("hello", fork(field::get).getOrThrow())
|
||||
field.set(null) // The executor thread is now considered leaked.
|
||||
fork {
|
||||
val leakedThreadName = Thread.currentThread().name
|
||||
globalThreadCreationMethod {
|
||||
verifyNoMoreInteractions(log)
|
||||
withSingleThreadExecutor {
|
||||
verify(log).warn(argThat { contains(leakedThreadName) })
|
||||
// In practice the new thread is for example a static thread we can't get rid of:
|
||||
assertNull(fork(field::get).getOrThrow())
|
||||
}
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-leaked thread does not propagate holder to global thread, without warning`() {
|
||||
val field = inheritableThreadLocalToggleField<String>()
|
||||
field.set("hello")
|
||||
withSingleThreadExecutor {
|
||||
fork {
|
||||
assertEquals("hello", field.get())
|
||||
globalThreadCreationMethod {
|
||||
withSingleThreadExecutor {
|
||||
assertNull(fork(field::get).getOrThrow())
|
||||
}
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.TestDataVendingFlow
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FetchAttachmentsFlow
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
@ -15,9 +16,12 @@ import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.persistence.NodeAttachmentService
|
||||
import net.corda.node.utilities.currentDBSession
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.ALICE_NAME
|
||||
import net.corda.testing.BOB_NAME
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.singleIdentity
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -62,14 +66,16 @@ class AttachmentSerializationTest {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var server: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var client: StartedNode<MockNetwork.MockNode>
|
||||
private lateinit var serverIdentity: Party
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockNet = MockNetwork()
|
||||
server = mockNet.createNode()
|
||||
client = mockNet.createNode()
|
||||
server = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||
client = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||
client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client.
|
||||
mockNet.runNetwork()
|
||||
serverIdentity = server.info.singleIdentity()
|
||||
}
|
||||
|
||||
@After
|
||||
@ -91,9 +97,7 @@ class AttachmentSerializationTest {
|
||||
private class ClientResult(internal val attachmentContent: String)
|
||||
|
||||
@InitiatingFlow
|
||||
private abstract class ClientLogic(server: StartedNode<*>) : FlowLogic<ClientResult>() {
|
||||
internal val server = server.info.chooseIdentity()
|
||||
|
||||
private abstract class ClientLogic(val serverIdentity: Party) : FlowLogic<ClientResult>() {
|
||||
@Suspendable
|
||||
internal fun communicate(serverSession: FlowSession) {
|
||||
serverSession.sendAndReceive<String>("ping one").unwrap { assertEquals("pong", it) }
|
||||
@ -112,30 +116,30 @@ class AttachmentSerializationTest {
|
||||
override val signers get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
private class CustomAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash, private val customContent: String) : ClientLogic(server) {
|
||||
private class CustomAttachmentLogic(serverIdentity: Party, private val attachmentId: SecureHash, private val customContent: String) : ClientLogic(serverIdentity) {
|
||||
@Suspendable
|
||||
override fun getAttachmentContent(): String {
|
||||
val customAttachment = CustomAttachment(attachmentId, customContent)
|
||||
val session = initiateFlow(server)
|
||||
val session = initiateFlow(serverIdentity)
|
||||
communicate(session)
|
||||
return customAttachment.customContent
|
||||
}
|
||||
}
|
||||
|
||||
private class OpenAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash) : ClientLogic(server) {
|
||||
private class OpenAttachmentLogic(serverIdentity: Party, private val attachmentId: SecureHash) : ClientLogic(serverIdentity) {
|
||||
@Suspendable
|
||||
override fun getAttachmentContent(): String {
|
||||
val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!!
|
||||
val session = initiateFlow(server)
|
||||
val session = initiateFlow(serverIdentity)
|
||||
communicate(session)
|
||||
return localAttachment.extractContent()
|
||||
}
|
||||
}
|
||||
|
||||
private class FetchAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash) : ClientLogic(server) {
|
||||
private class FetchAttachmentLogic(serverIdentity: Party, private val attachmentId: SecureHash) : ClientLogic(serverIdentity) {
|
||||
@Suspendable
|
||||
override fun getAttachmentContent(): String {
|
||||
val serverSession = initiateFlow(server)
|
||||
val serverSession = initiateFlow(serverIdentity)
|
||||
val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), serverSession)).downloaded
|
||||
serverSession.send(FetchDataFlow.Request.End)
|
||||
communicate(serverSession)
|
||||
@ -166,14 +170,14 @@ class AttachmentSerializationTest {
|
||||
@Test
|
||||
fun `custom (and non-persisted) attachment should be saved in checkpoint`() {
|
||||
val attachmentId = SecureHash.sha256("any old data")
|
||||
launchFlow(CustomAttachmentLogic(server, attachmentId, "custom"), 1)
|
||||
launchFlow(CustomAttachmentLogic(serverIdentity, attachmentId, "custom"), 1)
|
||||
assertEquals("custom", rebootClientAndGetAttachmentContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom attachment should be saved in checkpoint even if its data was persisted`() {
|
||||
val attachmentId = client.saveAttachment("genuine")
|
||||
launchFlow(CustomAttachmentLogic(server, attachmentId, "custom"), 1)
|
||||
launchFlow(CustomAttachmentLogic(serverIdentity, attachmentId, "custom"), 1)
|
||||
client.hackAttachment(attachmentId, "hacked") // Should not be reloaded, checkAttachmentsOnLoad may cause next line to blow up if client attempts it.
|
||||
assertEquals("custom", rebootClientAndGetAttachmentContent())
|
||||
}
|
||||
@ -182,7 +186,7 @@ class AttachmentSerializationTest {
|
||||
fun `only the hash of a regular attachment should be saved in checkpoint`() {
|
||||
val attachmentId = client.saveAttachment("genuine")
|
||||
client.attachments.checkAttachmentsOnLoad = false // Cached by AttachmentImpl.
|
||||
launchFlow(OpenAttachmentLogic(server, attachmentId), 1)
|
||||
launchFlow(OpenAttachmentLogic(serverIdentity, attachmentId), 1)
|
||||
client.hackAttachment(attachmentId, "hacked")
|
||||
assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) // Pass in false to allow non-genuine data to be loaded.
|
||||
}
|
||||
@ -190,7 +194,7 @@ class AttachmentSerializationTest {
|
||||
@Test
|
||||
fun `only the hash of a FetchAttachmentsFlow attachment should be saved in checkpoint`() {
|
||||
val attachmentId = server.saveAttachment("genuine")
|
||||
launchFlow(FetchAttachmentLogic(server, attachmentId), 2, sendData = true)
|
||||
launchFlow(FetchAttachmentLogic(serverIdentity, attachmentId), 2, sendData = true)
|
||||
client.hackAttachment(attachmentId, "hacked")
|
||||
assertEquals("hacked", rebootClientAndGetAttachmentContent(false))
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ class TransactionSerializationTests {
|
||||
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY)
|
||||
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY)
|
||||
|
||||
val megaCorpServices = MockServices(listOf("net.corda.core.serialization"), MEGA_CORP_KEY)
|
||||
val notaryServices = MockServices(listOf("net.corda.core.serialization"), DUMMY_NOTARY_KEY)
|
||||
val megaCorpServices = MockServices(listOf("net.corda.core.serialization"), MEGA_CORP.name, MEGA_CORP_KEY)
|
||||
val notaryServices = MockServices(listOf("net.corda.core.serialization"), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||
lateinit var tx: TransactionBuilder
|
||||
|
||||
@Before
|
||||
|
@ -2,9 +2,13 @@ package net.corda.core.transactions
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.testing.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.singleIdentity
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@ -18,6 +22,7 @@ class LedgerTransactionQueryTests {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val services: MockServices = MockServices()
|
||||
private val identity: Party = services.myInfo.singleIdentity()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -66,8 +71,8 @@ class LedgerTransactionQueryTests {
|
||||
tx.addInputState(makeDummyStateAndRef(i.toString()))
|
||||
tx.addOutputState(makeDummyState(i), DummyContract.PROGRAM_ID)
|
||||
tx.addOutputState(makeDummyState(i.toString()), DummyContract.PROGRAM_ID)
|
||||
tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.chooseIdentity().owningKey))
|
||||
tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.chooseIdentity().owningKey))
|
||||
tx.addCommand(Commands.Cmd1(i), listOf(identity.owningKey))
|
||||
tx.addCommand(Commands.Cmd2(i), listOf(identity.owningKey))
|
||||
}
|
||||
return tx.toLedgerTransaction(services)
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.testing.MEGA_CORP
|
||||
import net.corda.testing.MINI_CORP
|
||||
import net.corda.testing.SerializationEnvironmentRule
|
||||
import net.corda.testing.ledger
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
@ -17,6 +19,9 @@ import java.time.temporal.ChronoUnit
|
||||
val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
|
||||
|
||||
class TransactionEncumbranceTests {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
val defaultIssuer = MEGA_CORP.ref(1)
|
||||
|
||||
val state = Cash.State(
|
||||
|
@ -1,12 +1,13 @@
|
||||
Building a CorDapp
|
||||
==================
|
||||
CorDapps
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
cordapp-overview
|
||||
writing-cordapps
|
||||
writing-a-cordapp
|
||||
cordapp-build-systems
|
||||
building-against-master
|
||||
corda-api
|
||||
flow-cookbook
|
||||
cheat-sheet
|
@ -8,6 +8,9 @@ UNRELEASED
|
||||
----------
|
||||
* ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime.
|
||||
|
||||
* ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain
|
||||
strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface.
|
||||
|
||||
* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows.
|
||||
|
||||
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
|
||||
@ -77,6 +80,13 @@ UNRELEASED
|
||||
* Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This is
|
||||
needed to allow new ``node_info_hash`` column to be added for the network map redesign work.
|
||||
|
||||
* Added an overload of ``TransactionWithSignatures.verifySignaturesExcept`` which takes in a collection of ``PublicKey``s.
|
||||
|
||||
* Replaced node configuration parameter ``certificateSigningService`` with ``compatibilityZoneURL``, which is Corda
|
||||
compatibility zone network management service's address.
|
||||
|
||||
* ``waitForAllNodesToFinish()`` method in ``DriverDSLExposedInterface`` has instead become a parameter on driver creation.
|
||||
|
||||
.. _changelog_v1:
|
||||
|
||||
Release 1.0
|
||||
|
@ -45,8 +45,7 @@ Simple Notary configuration file.
|
||||
}
|
||||
useHTTPS : false
|
||||
devMode : true
|
||||
// Certificate signing service will be hosted by R3 in the near future.
|
||||
//certificateSigningService : "https://testnet.certificate.corda.net"
|
||||
compatibilityZoneURL : "https://cz.corda.net"
|
||||
|
||||
Fields
|
||||
------
|
||||
@ -141,8 +140,8 @@ path to the node's base directory.
|
||||
attempt to discover its externally visible IP address first by looking for any public addresses on its network
|
||||
interfaces, and then by sending an IP discovery request to the network map service. Set to ``false`` to disable.
|
||||
|
||||
:certificateSigningService: Certificate Signing Server address. It is used by the certificate signing request utility to
|
||||
obtain SSL certificate. (See :doc:`permissioning` for more information.)
|
||||
:compatibilityZoneURL: The root address of Corda compatibility zone network management services, it is used by the Corda node to register with the network and
|
||||
obtain Corda node certificate, (See :doc:`permissioning` for more information.) and also used by the node to obtain network map information.
|
||||
|
||||
:jvmArgs: An optional list of JVM args, as strings, which replace those inherited from the command line when launching via ``corda.jar``
|
||||
only. e.g. ``jvmArgs = [ "-Xmx220m", "-Xms220m", "-XX:+UseG1GC" ]``
|
||||
|
@ -6,9 +6,9 @@ Corda nodes
|
||||
|
||||
deploying-a-node
|
||||
running-a-node
|
||||
corda-configuration-file
|
||||
clientrpc
|
||||
shell
|
||||
node-database
|
||||
node-administration
|
||||
corda-configuration-file
|
||||
out-of-process-verification
|
@ -1,47 +1,30 @@
|
||||
CorDapp Build Systems
|
||||
=====================
|
||||
Building a CorDapp
|
||||
==================
|
||||
|
||||
Cordapps run on the Corda platform and integrate with it and each other. To learn more about the basics of a Cordapp
|
||||
please read :doc:`cordapp-overview`. To learn about writing a Cordapp as a developer please read :doc:`writing-cordapps`.
|
||||
.. contents::
|
||||
|
||||
This article will specifically deal with how to build cordapps, specifically with Gradle.
|
||||
Cordapps run on the Corda platform and integrate with it and each other. This article explains how to build CorDapps.
|
||||
To learn what a CorDapp is, please read :doc:`cordapp-overview`.
|
||||
|
||||
CorDapp JAR format
|
||||
------------------
|
||||
CorDapp format
|
||||
--------------
|
||||
A CorDapp is a semi-fat JAR that contains all of the CorDapp's dependencies *except* the Corda core libraries and any
|
||||
other CorDapps it depends on.
|
||||
|
||||
The first step to integrating a Cordapp with Corda is to ensure it is in the correct format. The correct format of a JAR
|
||||
is a semi-fat JAR that contains all of its own dependencies *except* the Corda core libraries and other Cordapps.
|
||||
For example, if a Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons``, then the Cordapp
|
||||
JAR will contain:
|
||||
|
||||
For example if your Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons`` then the Cordapp
|
||||
JAR will contain all classes and resources from the ``apache-commons`` JAR and its dependencies and *nothing* from the
|
||||
other two JARs.
|
||||
* All classes and resources from the ``apache-commons`` JAR and its dependencies
|
||||
* *Nothing* from the other two JARs
|
||||
|
||||
.. note:: The rest of this tutorial assumes you are using ``gradle``, the ``cordapp`` plugin and have forked from
|
||||
one of our cordapp templates.
|
||||
Build tools
|
||||
-----------
|
||||
In the instructions that follow, we assume you are using ``gradle`` and the ``cordformation`` plugin to build your
|
||||
CorDapp. See the `example build file <https://github.com/corda/cordapp-template-kotlin/blob/release-V1/build.gradle>`_
|
||||
from the CorDapp template.
|
||||
|
||||
The ``jar`` task included by default in the cordapp templates will automatically build your JAR in this format as long
|
||||
as your dependencies are correctly set.
|
||||
|
||||
The filename of the jar must include some sort of unique identifier to deduplicate it from other releases of the same
|
||||
CorDapp. This is typically done by appending the version string. It should not change once the jar has been deployed on
|
||||
a node. If it is then make sure no one is checking ``FlowContext.appName`` (see :doc:`versioning`).
|
||||
|
||||
Building against Corda
|
||||
----------------------
|
||||
|
||||
To build against Corda you must do the following to your ``build.gradle``;
|
||||
|
||||
* Add the ``net.corda:corda:<version>`` JAR as a ``cordaRuntime`` dependency.
|
||||
* Add each compile dependency (eg ``corda-core``) as a ``cordaCompile`` dependency.
|
||||
|
||||
To make use of the Corda test facilities you must;
|
||||
|
||||
* Add ``net.corda:corda-test-utils:<version>`` as a ``testCompile`` dependency (eg; a default Java/Kotlin compile task).
|
||||
|
||||
.. warning:: Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency.
|
||||
|
||||
These configurations work by the ``cordapp`` plugin adding ``cordaCompile`` as a new configuration that ``compile``
|
||||
extends from, and ``cordaRuntime`` which ``runtime`` extends from.
|
||||
Setting your dependencies
|
||||
-------------------------
|
||||
|
||||
Choosing your Corda version
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -49,11 +32,15 @@ The following two lines of the ``build.gradle`` file define the Corda version us
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
ext.corda_release_version = '0.13.0'
|
||||
ext.corda_gradle_plugins_version = '0.13.3'
|
||||
ext.corda_release_version = '1.0.0'
|
||||
ext.corda_gradle_plugins_version = '1.0.0'
|
||||
|
||||
In this case, our CorDapp will use the Milestone 13 release of Corda, and version 13.3 of the Corda gradle plugins. You
|
||||
can find the latest published version of both here: https://bintray.com/r3/corda.
|
||||
In this case, our CorDapp will use:
|
||||
|
||||
* Version 1.0 of Corda
|
||||
* Version 1.0 of the Corda gradle plugins
|
||||
|
||||
You can find the latest published version of both here: https://bintray.com/r3/corda.
|
||||
|
||||
``corda_gradle_plugins_versions`` are given in the form ``major.minor.patch``. You should use the same ``major`` and
|
||||
``minor`` versions as the Corda version you are using, and the latest ``patch`` version. A list of all the available
|
||||
@ -61,34 +48,39 @@ versions can be found here: https://bintray.com/r3/corda/cordapp.
|
||||
|
||||
In certain cases, you may also wish to build against the unstable Master branch. See :doc:`building-against-master`.
|
||||
|
||||
Building against CorDapps
|
||||
-------------------------
|
||||
Corda dependencies
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
The ``cordformation`` plugin adds:
|
||||
|
||||
To build against a Cordapp you must add it as a ``cordapp`` dependency to your ``build.gradle``.
|
||||
* ``cordaCompile`` as a new configuration that ``compile`` extends from
|
||||
* ``cordaRuntime`` which ``runtime`` extends from
|
||||
|
||||
Other Dependencies
|
||||
------------------
|
||||
To build against Corda you must add the following to your ``build.gradle`` file;
|
||||
|
||||
* The ``net.corda:corda:<version>`` JAR as a ``cordaRuntime`` dependency
|
||||
* Each compile dependency (eg ``corda-core``) as a ``cordaCompile`` dependency
|
||||
|
||||
To use Corda's test facilities you must add ``net.corda:corda-test-utils:<version>`` as a ``testCompile`` dependency
|
||||
(i.e. a default Java/Kotlin test compile task).
|
||||
|
||||
.. warning:: Never include ``corda-test-utils`` as a ``compile`` or ``cordaCompile`` dependency.
|
||||
|
||||
Dependencies on other CorDapps
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Sometimes, a CorDapp you build will depend on states, contracts or flows defined in another CorDapp. You must include
|
||||
the CorDapp your CorDapp depends upon as a ``cordapp`` dependency in your ``build.gradle`` file.
|
||||
|
||||
Other dependencies
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
If your CorDapps have any additional external dependencies, they can be specified like normal Kotlin/Java dependencies
|
||||
in Gradle. See the example below, specifically the ``apache-commons`` include.
|
||||
|
||||
For further information about managing dependencies, see
|
||||
`the Gradle docs <https://docs.gradle.org/current/userguide/dependency_management.html>`_.
|
||||
|
||||
Installing CorDapps
|
||||
-------------------
|
||||
|
||||
At runtime, nodes will load any CorDapp JARs present in their ``cordapps`` folder. Therefore in order to install a CorDapp to
|
||||
a node the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder, where ``node_dir`` is the folder in which the
|
||||
node's JAR and configuration files are stored).
|
||||
|
||||
The ``deployNodes`` gradle task, if correctly configured, will automatically place your CorDapp JAR as well as any
|
||||
dependent cordapp JARs specified into the directory automatically.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The following is a sample of what a gradle dependencies block for a cordapp could look like. The cordapp template
|
||||
^^^^^^^
|
||||
The following is a sample of what a gradle dependencies block for a CorDapp could look like. The CorDapp template
|
||||
is already correctly configured and this is for reference only;
|
||||
|
||||
.. container:: codeset
|
||||
@ -108,6 +100,9 @@ is already correctly configured and this is for reference only;
|
||||
testCompile "net.corda:corda-test-utils:$corda_release_version"
|
||||
|
||||
// Corda Plugins: dependent flows and services
|
||||
// Identifying a CorDapp by its module in the same project.
|
||||
cordapp project(":cordapp-contracts-states")
|
||||
// Identifying a CorDapp by its fully-qualified name.
|
||||
cordapp "net.corda:bank-of-corda-demo:1.0"
|
||||
|
||||
// Some other dependencies
|
||||
@ -118,3 +113,29 @@ is already correctly configured and this is for reference only;
|
||||
compile "org.apache.commons:commons-lang3:3.6"
|
||||
}
|
||||
|
||||
Creating the CorDapp JAR
|
||||
------------------------
|
||||
The gradle ``jar`` task included in the CorDapp template build file will automatically build your CorDapp JAR correctly
|
||||
as long as your dependencies are set correctly.
|
||||
|
||||
Note that the hash of the resulting CorDapp JAR is not deterministic, as it depends on variables such as the timestamp
|
||||
at creation. Nodes running the same CorDapp must therefore ensure they are using the exact same CorDapp jar, and not
|
||||
different versions of the JAR created from identical sources.
|
||||
|
||||
The filename of the JAR must include a unique identifier to deduplicate it from other releases of the same CorDapp.
|
||||
This is typically done by appending the version string to the CorDapp's name. This unique identifier should not change
|
||||
once the JAR has been deployed on a node. If it does, make sure no one is relying on ``FlowContext.appName`` in their
|
||||
flows (see :doc:`versioning`).
|
||||
|
||||
Installing the CorDapp jar
|
||||
--------------------------
|
||||
|
||||
.. note:: Before installing a CorDapp, you must create one or more nodes to install it on. For instructions, please see
|
||||
:doc:`deploying-a-node`.
|
||||
|
||||
At runtime, nodes will load any CorDapps present in their ``cordapps`` folder. Therefore in order to install a CorDapp on
|
||||
a node, the CorDapp JAR must be added to the ``<node_dir>/cordapps/`` folder, where ``node_dir`` is the folder in which
|
||||
the node's JAR and configuration files are stored.
|
||||
|
||||
The ``deployNodes`` gradle task, if correctly configured, will automatically place your CorDapp JAR as well as any
|
||||
dependent CorDapp JARs specified into the ``cordapps`` folder automatically.
|
@ -1,25 +1,30 @@
|
||||
What is a CorDapp?
|
||||
==================
|
||||
|
||||
Corda is a platform. Its functionality is extended by developers through the writing of Corda distributed
|
||||
applications (CorDapps). CorDapps are installed at the level of the individual node, rather than on the network
|
||||
itself.
|
||||
|
||||
Each CorDapp allows a node to handle new business processes, for example asset trading (see :ref:`irs-demo`).
|
||||
It does so by defining new flows on the node that, once started by the node owner, conduct the process of negotiating
|
||||
a specific ledger update with other nodes on the network. The node's owner can then start these flows as required,
|
||||
either through remote procedure calls (RPC) or HTTP requests that leverage the RPC interface.
|
||||
CorDapps (Corda Distributed Applications) are distributed applications that run on the Corda platform. The goal of a
|
||||
CorDapp is to allow nodes to reach agreement on updates to the ledger. They achieve this goal by defining flows that
|
||||
Corda node owners can invoke through RPC calls:
|
||||
|
||||
.. image:: resources/node-diagram.png
|
||||
|
||||
CorDapp developers will usually define not only these flows, but also any states and contracts that these flows use.
|
||||
They will also have to define any web APIs that will run on the node's standalone web server, any static web content,
|
||||
and any new services that they want their CorDapp to offer.
|
||||
CorDapps are made up of the following key components:
|
||||
|
||||
CorDapps are made up of definitions for the following components:
|
||||
* States, defining the facts over which agreement is reached (see :doc:`Key Concepts - States <key-concepts-states>`)
|
||||
* Contracts, defining what constitutes a valid ledger update (see
|
||||
:doc:`Key Concepts - Contracts <key-concepts-contracts>`)
|
||||
* Services, providing long-lived utilities within the node
|
||||
* Serialisation whitelists, restricting what types your node will receive off the wire
|
||||
|
||||
* States
|
||||
* Contracts
|
||||
* Flows
|
||||
* Web APIs and static web content
|
||||
* Services
|
||||
Each CorDapp is installed at the level of the individual node, rather than on the network itself. For example, a node
|
||||
owner may choose to install the Bond Trading CorDapp, with the following components:
|
||||
|
||||
* A ``BondState``, used to represent bonds as shared facts on the ledger
|
||||
* A ``BondContract``, used to govern which ledger updates involving ``BondState`` states are valid
|
||||
* Three flows:
|
||||
|
||||
* An ``IssueBondFlow``, allowing new ``BondState`` states to be issued onto the ledger
|
||||
* A ``TradeBondFlow``, allowing existing ``BondState`` states to be bought and sold on the ledger
|
||||
* An ``ExitBondFlow``, allowing existing ``BondState`` states to be exited from the ledger
|
||||
|
||||
After installing this CorDapp, the node owner will be able to use the flows defined by the CorDapp to agree ledger
|
||||
updates related to issuance, sale, purchase and exit of bonds.
|
@ -1,97 +1,141 @@
|
||||
Deploying a node
|
||||
================
|
||||
|
||||
Using Gradle to build nodes
|
||||
---------------------------
|
||||
Nodes are usually built using a Gradle task. The canonical Gradle file for building nodes is the one used by the
|
||||
CorDapp template. Both a `Java version <https://github.com/corda/cordapp-template-java/blob/master/build.gradle>`_ and
|
||||
a `Kotlin version <https://github.com/corda/cordapp-template-kotlin/blob/master/build.gradle>`_ are available.
|
||||
Node structure
|
||||
--------------
|
||||
Each Corda node has the following structure:
|
||||
|
||||
Cordform is the local node deployment system for CorDapps. The nodes generated are intended for experimenting,
|
||||
debugging, and testing node configurations, but not for production or testnet deployment.
|
||||
.. sourcecode:: none
|
||||
|
||||
Here is an example Gradle task called ``deployNodes`` that uses the Cordform plugin to deploy three nodes, plus a
|
||||
notary node:
|
||||
.
|
||||
├── certificates // The node's doorman certificates
|
||||
├── corda-webserver.jar // The built-in node webserver
|
||||
├── corda.jar // The core Corda libraries
|
||||
├── logs // The node logs
|
||||
├── node.conf // The node's configuration files
|
||||
├── persistence.mv.db // The node's database
|
||||
└── cordapps // The CorDapps jars installed on the node
|
||||
|
||||
The node is configured by editing its ``node.conf`` file. You install CorDapps on the node by dropping the CorDapp JARs
|
||||
into the ``cordapps`` folder.
|
||||
|
||||
Node naming
|
||||
-----------
|
||||
A node's name must be a valid X500 name that obeys the following additional constraints:
|
||||
|
||||
* The fields of the name have the following maximum character lengths:
|
||||
|
||||
* Common name: 64
|
||||
* Organisation: 128
|
||||
* Organisation unit: 64
|
||||
* Locality: 64
|
||||
* State: 64
|
||||
|
||||
* The country code is a valid ISO 3166-1 two letter code in upper-case
|
||||
|
||||
* The organisation, locality and country attributes are present
|
||||
|
||||
* The organisation field of the name obeys the following constraints:
|
||||
|
||||
* Has at least two letters
|
||||
* No leading or trailing whitespace
|
||||
* No double-spacing
|
||||
* Upper-case first letter
|
||||
* Does not contain the words "node" or "server"
|
||||
* Does not include the characters ',' or '=' or '$' or '"' or '\'' or '\\'
|
||||
* Is in NFKC normalization form
|
||||
* Only the latin, common and inherited unicode scripts are supported
|
||||
|
||||
The deployNodes task
|
||||
--------------------
|
||||
The CorDapp template defines a ``deployNodes`` task that allows you to automatically generate and configure a set of
|
||||
nodes:
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||
directory "./build/nodes"
|
||||
networkMap "O=Controller,L=London,C=GB"
|
||||
node {
|
||||
name "O=Controller,OU=corda,L=London,C=UK"
|
||||
name "O=Controller,L=London,C=GB"
|
||||
// The notary will offer a validating notary service.
|
||||
notary = [validating : true]
|
||||
p2pPort 10002
|
||||
rpcPort 10003
|
||||
webPort 10004
|
||||
cordapps = []
|
||||
p2pPort 10002
|
||||
rpcPort 10003
|
||||
// No webport property, so no webserver will be created.
|
||||
h2Port 10004
|
||||
sshdPort 22
|
||||
// Includes the corda-finance CorDapp on our node.
|
||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||
}
|
||||
node {
|
||||
name "CN=NodeA,O=NodeA,L=London,C=UK"
|
||||
p2pPort 10005
|
||||
rpcPort 10006
|
||||
webPort 10007
|
||||
cordapps = []
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||
name "O=PartyA,L=London,C=GB"
|
||||
advertisedServices = []
|
||||
p2pPort 10005
|
||||
rpcPort 10006
|
||||
webPort 10007
|
||||
h2Port 10008
|
||||
sshdPort 22
|
||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||
// Grants user1 all RPC permissions.
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
|
||||
}
|
||||
node {
|
||||
name "CN=NodeB,O=NodeB,L=New York,C=US"
|
||||
p2pPort 10008
|
||||
rpcPort 10009
|
||||
webPort 10010
|
||||
cordapps = []
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||
}
|
||||
// Example of explicit addresses being used.
|
||||
node {
|
||||
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
|
||||
p2pAddress "localhost:10011"
|
||||
rpcAddress "localhost:10012"
|
||||
webAddress "localhost:10013"
|
||||
cordapps = []
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
|
||||
name "O=PartyB,L=New York,C=US"
|
||||
advertisedServices = []
|
||||
p2pPort 10009
|
||||
rpcPort 10010
|
||||
webPort 10011
|
||||
h2Port 10012
|
||||
sshdPort 22
|
||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||
// Grants user1 the ability to start the MyFlow flow.
|
||||
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
|
||||
}
|
||||
}
|
||||
|
||||
You can extend ``deployNodes`` to generate any number of nodes you like.
|
||||
Running this task will create three nodes in the ``build/nodes`` folder:
|
||||
|
||||
* A ``Controller`` node that:
|
||||
|
||||
* Serves as the network map
|
||||
* Offers a validating notary service
|
||||
* Will not have a webserver (since ``webPort`` is not defined)
|
||||
* Is running the ``corda-finance`` CorDapp
|
||||
|
||||
* ``PartyA`` and ``PartyB`` nodes that:
|
||||
|
||||
* Are pointing at the ``Controller`` as the network map service
|
||||
* Are not offering any services
|
||||
* Will have a webserver (since ``webPort`` is defined)
|
||||
* Are running the ``corda-finance`` CorDapp
|
||||
* Have an RPC user, ``user1``, that can be used to log into the node via RPC
|
||||
|
||||
Additionally, all three nodes will include any CorDapps defined in the project's source folders, even though these
|
||||
CorDapps are not listed in each node's ``cordapps`` entry. This means that running the ``deployNodes`` task from the
|
||||
template CorDapp, for example, would automatically build and add the template CorDapp to each node.
|
||||
|
||||
You can extend ``deployNodes`` to generate additional nodes. The only requirement is that you must specify
|
||||
a single node to run the network map service, by putting their name in the ``networkMap`` field.
|
||||
|
||||
.. warning:: When adding nodes, make sure that there are no port clashes!
|
||||
|
||||
If your CorDapp is written in Java, you should also add the following Gradle snippet so that you can pass named arguments to your flows via the Corda shell:
|
||||
|
||||
.. sourcecode:: groovy
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-parameters"
|
||||
}
|
||||
|
||||
Any CorDapps defined in the project's source folders are also automatically registered with all the nodes defined in
|
||||
``deployNodes``, even if the CorDapps are not listed in each node's ``cordapps`` entry.
|
||||
|
||||
Deploying your nodes
|
||||
--------------------
|
||||
You deploy a set of nodes by running your ``build.gradle`` file's Cordform task. For example, if we were using the
|
||||
standard ``deployNodes`` task defined above, we'd create our nodes by running the following commands in a terminal
|
||||
window from the root of the project:
|
||||
Running deployNodes
|
||||
-------------------
|
||||
To create the nodes defined in our ``deployNodes`` task, we'd run the following command in a terminal window from the
|
||||
root of the project:
|
||||
|
||||
* Unix/Mac OSX: ``./gradlew deployNodes``
|
||||
* Windows: ``gradlew.bat deployNodes``
|
||||
|
||||
After the build process has finished, you will find the newly-built nodes under ``kotlin-source/build/nodes``. There
|
||||
will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on Windows) to
|
||||
run all the nodes at once. Each node in the ``nodes`` folder has the following structure:
|
||||
|
||||
.. sourcecode:: none
|
||||
|
||||
. nodeName
|
||||
├── corda.jar // The Corda runtime
|
||||
├── node.conf // The node's configuration
|
||||
├── cordapps // Any installed CorDapps
|
||||
└── additional-node-infos // Directory containing all the addresses and certificates of the other nodes.
|
||||
This will create the nodes in the ``build/nodes`` folder.
|
||||
|
||||
.. note:: During the build process each node generates a NodeInfo file which is written in its own root directory,
|
||||
the plug-in proceeds and copies each node NodeInfo to every other node ``additional-node-infos`` directory.
|
||||
The NodeInfo file contains a node hostname and port, legal name and security certificate.
|
||||
the plug-in proceeds and copies each node NodeInfo to every other node ``additional-node-infos`` directory.
|
||||
The NodeInfo file contains a node hostname and port, legal name and security certificate.
|
||||
|
||||
.. note:: Outside of development environments, do not store your node directories in the build folder.
|
||||
There will be a node folder generated for each node you defined, plus a ``runnodes`` shell script (or batch file on
|
||||
Windows) to run all the nodes at once. If you make any changes to your ``deployNodes`` task, you will need to re-run
|
||||
the task to see the changes take effect.
|
||||
|
||||
If you make any changes to your ``deployNodes`` task, you will need to re-run the task to see the changes take effect.
|
||||
You can now run the nodes by following the instructions in :doc:`Running a node <running-a-node>`.
|
@ -8,6 +8,7 @@ import net.corda.core.crypto.TransactionSignature;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.identity.CordaX500Name;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.identity.PartyAndCertificate;
|
||||
import net.corda.core.internal.FetchDataFlow;
|
||||
import net.corda.core.node.services.Vault;
|
||||
import net.corda.core.node.services.Vault.Page;
|
||||
@ -401,8 +402,8 @@ public class FlowCookbookJava {
|
||||
// DOCEND 29
|
||||
// We can also sign the transaction using a different public key:
|
||||
// DOCSTART 30
|
||||
PublicKey otherKey = getServiceHub().getKeyManagementService().freshKey();
|
||||
SignedTransaction onceSignedTx2 = getServiceHub().signInitialTransaction(txBuilder, otherKey);
|
||||
PartyAndCertificate otherIdentity = getServiceHub().getKeyManagementService().freshKeyAndCert(getOurIdentityAndCert(), false);
|
||||
SignedTransaction onceSignedTx2 = getServiceHub().signInitialTransaction(txBuilder, otherIdentity.getOwningKey());
|
||||
// DOCEND 30
|
||||
|
||||
// If instead this was a ``SignedTransaction`` that we'd received
|
||||
@ -412,9 +413,9 @@ public class FlowCookbookJava {
|
||||
SignedTransaction twiceSignedTx = getServiceHub().addSignature(onceSignedTx);
|
||||
// DOCEND 38
|
||||
// Or, if we wanted to use a different public key:
|
||||
PublicKey otherKey2 = getServiceHub().getKeyManagementService().freshKey();
|
||||
PartyAndCertificate otherIdentity2 = getServiceHub().getKeyManagementService().freshKeyAndCert(getOurIdentityAndCert(), false);
|
||||
// DOCSTART 39
|
||||
SignedTransaction twiceSignedTx2 = getServiceHub().addSignature(onceSignedTx, otherKey2);
|
||||
SignedTransaction twiceSignedTx2 = getServiceHub().addSignature(onceSignedTx, otherIdentity2.getOwningKey());
|
||||
// DOCEND 39
|
||||
|
||||
// We can also generate a signature over the transaction without
|
||||
@ -428,7 +429,7 @@ public class FlowCookbookJava {
|
||||
// DOCEND 40
|
||||
// And again, if we wanted to use a different public key:
|
||||
// DOCSTART 41
|
||||
TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2);
|
||||
TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherIdentity2.getOwningKey());
|
||||
// DOCEND 41
|
||||
|
||||
/*----------------------------
|
||||
|
@ -1,46 +0,0 @@
|
||||
package net.corda.docs.java.tutorial.helloworld;
|
||||
|
||||
// DOCSTART 01
|
||||
import net.corda.core.contracts.CommandData;
|
||||
import net.corda.core.contracts.CommandWithParties;
|
||||
import net.corda.core.contracts.Contract;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.transactions.LedgerTransaction;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.List;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
|
||||
public class IOUContract implements Contract {
|
||||
// Our Create command.
|
||||
public static class Create implements CommandData {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(LedgerTransaction tx) {
|
||||
final CommandWithParties<Create> command = requireSingleCommand(tx.getCommands(), Create.class);
|
||||
|
||||
requireThat(check -> {
|
||||
// Constraints on the shape of the transaction.
|
||||
check.using("No inputs should be consumed when issuing an IOU.", tx.getInputs().isEmpty());
|
||||
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
|
||||
|
||||
// IOU-specific constraints.
|
||||
final IOUState out = tx.outputsOfType(IOUState.class).get(0);
|
||||
final Party lender = out.getLender();
|
||||
final Party borrower = out.getBorrower();
|
||||
check.using("The IOU's value must be non-negative.", out.getValue() > 0);
|
||||
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
|
||||
|
||||
// Constraints on the signers.
|
||||
final List<PublicKey> signers = command.getSigners();
|
||||
check.using("There must only be one signer.", signers.size() == 1);
|
||||
check.using("The signer must be the lender.", signers.contains(lender.getOwningKey()));
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -3,13 +3,15 @@ package net.corda.docs.java.tutorial.helloworld;
|
||||
// DOCSTART 01
|
||||
import co.paralleluniverse.fibers.Suspendable;
|
||||
import net.corda.core.contracts.Command;
|
||||
import net.corda.core.contracts.StateAndContract;
|
||||
import net.corda.core.contracts.CommandData;
|
||||
import net.corda.core.flows.*;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.transactions.SignedTransaction;
|
||||
import net.corda.core.transactions.TransactionBuilder;
|
||||
import net.corda.core.utilities.ProgressTracker;
|
||||
|
||||
import static net.corda.docs.java.tutorial.helloworld.TemplateContract.TEMPLATE_CONTRACT_ID;
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
public class IOUFlow extends FlowLogic<Void> {
|
||||
@ -40,21 +42,15 @@ public class IOUFlow extends FlowLogic<Void> {
|
||||
// We retrieve the notary identity from the network map.
|
||||
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
|
||||
|
||||
// We create a transaction builder.
|
||||
final TransactionBuilder txBuilder = new TransactionBuilder();
|
||||
txBuilder.setNotary(notary);
|
||||
|
||||
// We create the transaction components.
|
||||
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
|
||||
String outputContract = IOUContract.class.getName();
|
||||
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract);
|
||||
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
|
||||
CommandData cmdType = new TemplateContract.Commands.Action();
|
||||
Command cmd = new Command<>(cmdType, getOurIdentity().getOwningKey());
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd);
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(getServiceHub());
|
||||
// We create a transaction builder and add the components.
|
||||
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
|
||||
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
|
||||
.addCommand(cmd);
|
||||
|
||||
// Signing the transaction.
|
||||
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
|
||||
|
@ -0,0 +1,21 @@
|
||||
package net.corda.docs.java.tutorial.helloworld;
|
||||
|
||||
import net.corda.core.contracts.CommandData;
|
||||
import net.corda.core.contracts.Contract;
|
||||
import net.corda.core.transactions.LedgerTransaction;
|
||||
|
||||
public class TemplateContract implements Contract {
|
||||
// This is used to identify our contract when building a transaction.
|
||||
public static final String TEMPLATE_CONTRACT_ID = "com.template.TemplateContract";
|
||||
|
||||
/**
|
||||
* A transaction is considered valid if the verify() function of the contract of each of the transaction's input
|
||||
* and output states does not throw an exception.
|
||||
*/
|
||||
@Override
|
||||
public void verify(LedgerTransaction tx) {}
|
||||
|
||||
public interface Commands extends CommandData {
|
||||
class Action implements Commands {}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ import net.corda.core.utilities.OpaqueBytes;
|
||||
import net.corda.finance.contracts.ICommercialPaperState;
|
||||
import net.corda.finance.contracts.JavaCommercialPaper;
|
||||
import net.corda.finance.contracts.asset.Cash;
|
||||
import net.corda.testing.SerializationEnvironmentRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@ -19,6 +21,8 @@ import static net.corda.testing.NodeTestUtils.transaction;
|
||||
import static net.corda.testing.TestConstants.*;
|
||||
|
||||
public class CommercialPaperTest {
|
||||
@Rule
|
||||
public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule();
|
||||
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{123});
|
||||
|
||||
// DOCSTART 1
|
||||
|
@ -13,7 +13,6 @@ import java.util.List;
|
||||
|
||||
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||
// DOCEND 01
|
||||
|
||||
public class IOUContract implements Contract {
|
||||
// Our Create command.
|
||||
@ -22,7 +21,7 @@ public class IOUContract implements Contract {
|
||||
|
||||
@Override
|
||||
public void verify(LedgerTransaction tx) {
|
||||
final CommandWithParties<net.corda.docs.java.tutorial.helloworld.IOUContract.Create> command = requireSingleCommand(tx.getCommands(), net.corda.docs.java.tutorial.helloworld.IOUContract.Create.class);
|
||||
final CommandWithParties<IOUContract.Create> command = requireSingleCommand(tx.getCommands(), IOUContract.Create.class);
|
||||
|
||||
requireThat(check -> {
|
||||
// Constraints on the shape of the transaction.
|
||||
@ -36,15 +35,14 @@ public class IOUContract implements Contract {
|
||||
check.using("The IOU's value must be non-negative.", out.getValue() > 0);
|
||||
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
|
||||
|
||||
// DOCSTART 02
|
||||
// Constraints on the signers.
|
||||
final List<PublicKey> signers = command.getSigners();
|
||||
check.using("There must be two signers.", signers.size() == 2);
|
||||
check.using("The borrower and lender must be signers.", signers.containsAll(
|
||||
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
|
||||
// DOCEND 02
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -48,7 +48,7 @@ fun main(args: Array<String>) {
|
||||
startFlow<CashExitFlow>(),
|
||||
invokeRpc(CordaRPCOps::nodeInfo)
|
||||
))
|
||||
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance")) {
|
||||
driver(driverDirectory = baseDirectory, extraCordappPackagesToScan = listOf("net.corda.finance"), waitForAllNodesToFinish = true) {
|
||||
val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
|
||||
// END 1
|
||||
|
||||
@ -96,7 +96,6 @@ fun main(args: Array<String>) {
|
||||
graph.display()
|
||||
}
|
||||
}
|
||||
waitForAllNodesToFinish()
|
||||
// END 5
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.node.services.Vault.Page
|
||||
import net.corda.core.node.services.queryBy
|
||||
@ -387,8 +388,8 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty:
|
||||
// DOCEND 29
|
||||
// We can also sign the transaction using a different public key:
|
||||
// DOCSTART 30
|
||||
val otherKey: PublicKey = serviceHub.keyManagementService.freshKey()
|
||||
val onceSignedTx2: SignedTransaction = serviceHub.signInitialTransaction(txBuilder, otherKey)
|
||||
val otherIdentity: PartyAndCertificate = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false)
|
||||
val onceSignedTx2: SignedTransaction = serviceHub.signInitialTransaction(txBuilder, otherIdentity.owningKey)
|
||||
// DOCEND 30
|
||||
|
||||
// If instead this was a ``SignedTransaction`` that we'd received
|
||||
@ -398,9 +399,9 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty:
|
||||
val twiceSignedTx: SignedTransaction = serviceHub.addSignature(onceSignedTx)
|
||||
// DOCEND 38
|
||||
// Or, if we wanted to use a different public key:
|
||||
val otherKey2: PublicKey = serviceHub.keyManagementService.freshKey()
|
||||
val otherIdentity2: PartyAndCertificate = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false)
|
||||
// DOCSTART 39
|
||||
val twiceSignedTx2: SignedTransaction = serviceHub.addSignature(onceSignedTx, otherKey2)
|
||||
val twiceSignedTx2: SignedTransaction = serviceHub.addSignature(onceSignedTx, otherIdentity2.owningKey)
|
||||
// DOCEND 39
|
||||
|
||||
// We can also generate a signature over the transaction without
|
||||
@ -414,7 +415,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty:
|
||||
// DOCEND 40
|
||||
// And again, if we wanted to use a different public key:
|
||||
// DOCSTART 41
|
||||
val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherKey2)
|
||||
val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherIdentity2.owningKey)
|
||||
// DOCEND 41
|
||||
|
||||
// In practice, however, the process of gathering every signature
|
||||
|
@ -1,33 +0,0 @@
|
||||
package net.corda.docs.tutorial.helloworld
|
||||
|
||||
// DOCSTART 01
|
||||
import net.corda.core.contracts.CommandData
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.requireSingleCommand
|
||||
import net.corda.core.contracts.requireThat
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
|
||||
class IOUContract : Contract {
|
||||
// Our Create command.
|
||||
class Create : CommandData
|
||||
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
val command = tx.commands.requireSingleCommand<Create>()
|
||||
|
||||
requireThat {
|
||||
// Constraints on the shape of the transaction.
|
||||
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
|
||||
"There should be one output state of type IOUState." using (tx.outputs.size == 1)
|
||||
|
||||
// IOU-specific constraints.
|
||||
val out = tx.outputsOfType<IOUState>().single()
|
||||
"The IOU's value must be non-negative." using (out.value > 0)
|
||||
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
|
||||
|
||||
// Constraints on the signers.
|
||||
"There must only be one signer." using (command.signers.toSet().size == 1)
|
||||
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -3,7 +3,6 @@ package net.corda.docs.tutorial.helloworld
|
||||
// DOCSTART 01
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.StateAndContract
|
||||
import net.corda.core.flows.FinalityFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
@ -11,7 +10,6 @@ import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import kotlin.reflect.jvm.jvmName
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
@ -27,25 +25,19 @@ class IOUFlow(val iouValue: Int,
|
||||
// We retrieve the notary identity from the network map.
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities[0]
|
||||
|
||||
// We create a transaction builder
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
|
||||
// We create the transaction components.
|
||||
val outputState = IOUState(iouValue, ourIdentity, otherParty)
|
||||
val outputContract = IOUContract::class.jvmName
|
||||
val outputContractAndState = StateAndContract(outputState, outputContract)
|
||||
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
|
||||
val cmd = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)
|
||||
|
||||
// We add the items to the builder.
|
||||
txBuilder.withItems(outputContractAndState, cmd)
|
||||
// We create a transaction builder and add the components.
|
||||
val txBuilder = TransactionBuilder(notary = notary)
|
||||
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
|
||||
.addCommand(cmd)
|
||||
|
||||
// Verifying the transaction.
|
||||
txBuilder.verify(serviceHub)
|
||||
|
||||
// Signing the transaction.
|
||||
// We sign the transaction.
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
||||
// Finalising the transaction.
|
||||
// We finalise the transaction.
|
||||
subFlow(FinalityFlow(signedTx))
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package net.corda.docs.tutorial.helloworld
|
||||
|
||||
import net.corda.core.contracts.CommandData
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
|
||||
val TEMPLATE_CONTRACT_ID = "com.template.TemplateContract"
|
||||
|
||||
open class TemplateContract : Contract {
|
||||
// A transaction is considered valid if the verify() function of the contract of each of the transaction's input
|
||||
// and output states does not throw an exception.
|
||||
override fun verify(tx: LedgerTransaction) {
|
||||
// Verification logic goes here.
|
||||
}
|
||||
|
||||
// Used to indicate the transaction's intent.
|
||||
interface Commands : CommandData {
|
||||
class Action : Commands
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -67,10 +68,7 @@ class TutorialMockNetwork {
|
||||
mockNet = MockNetwork()
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
|
||||
nodeB.registerInitiatedFlow(FlowB::class.java)
|
||||
|
||||
mockNet.runNetwork()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -8,11 +8,15 @@ import net.corda.finance.contracts.CommercialPaper
|
||||
import net.corda.finance.contracts.ICommercialPaperState
|
||||
import net.corda.finance.contracts.asset.CASH
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.contracts.asset.ownedBy
|
||||
import net.corda.testing.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class CommercialPaperTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
// DOCSTART 1
|
||||
fun getPaper(): ICommercialPaperState = CommercialPaper.State(
|
||||
issuance = MEGA_CORP.ref(123),
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.requireSingleCommand
|
||||
import net.corda.core.contracts.requireThat
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
// DOCEND 01
|
||||
|
||||
class IOUContract : Contract {
|
||||
// Our Create command.
|
||||
@ -25,12 +24,11 @@ class IOUContract : Contract {
|
||||
"The IOU's value must be non-negative." using (out.value > 0)
|
||||
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
|
||||
|
||||
// DOCSTART 02
|
||||
// Constraints on the signers.
|
||||
"There must be two signers." using (command.signers.toSet().size == 2)
|
||||
"The borrower and lender must be signers." using (command.signers.containsAll(listOf(
|
||||
out.borrower.owningKey, out.lender.owningKey)))
|
||||
// DOCEND 02
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// DOCEND 01
|
@ -2,17 +2,15 @@ package net.corda.docs
|
||||
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.*
|
||||
import net.corda.finance.contracts.getCashBalances
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
@ -27,17 +25,11 @@ class CustomVaultQueryTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(threadPerNode = true,
|
||||
cordappPackages = listOf(
|
||||
"net.corda.finance.contracts.asset",
|
||||
CashSchemaV1::class.packageName,
|
||||
"net.corda.docs"
|
||||
)
|
||||
)
|
||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.docs"))
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java)
|
||||
notary = nodeA.services.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -1,18 +1,16 @@
|
||||
package net.corda.docs
|
||||
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.*
|
||||
import net.corda.finance.contracts.getCashBalances
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.startFlow
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -26,11 +24,11 @@ class FxTransactionBuildTutorialTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName))
|
||||
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
||||
nodeA = mockNet.createPartyNode()
|
||||
nodeB = mockNet.createPartyNode()
|
||||
nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)
|
||||
notary = nodeA.services.getDefaultNotary()
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -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
|
||||
|
@ -168,9 +168,9 @@ The best way to check that everything is working fine is by taking a deeper look
|
||||
|
||||
Next, you should read through :doc:`Corda Key Concepts <key-concepts>` to understand how Corda works.
|
||||
|
||||
You'll then be ready to start writing your own CorDapps. Learn how to do this in the
|
||||
:doc:`Hello, World tutorial <hello-world-index>`. You'll want to refer to the :doc:`API docs <api-index>`, the
|
||||
By then, you'll be ready to start writing your own CorDapps. Learn how to do this in the
|
||||
:doc:`Hello, World tutorial <hello-world-introduction>`. You may want to refer to the :doc:`API docs <api-index>`, the
|
||||
:doc:`flow cookbook <flow-cookbook>` and the `samples <https://www.corda.net/samples/>`_ along the way.
|
||||
|
||||
If you encounter any issues, please see the :doc:`troubleshooting` page, or get in touch with us on the
|
||||
`forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.
|
||||
`forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.
|
@ -1,197 +0,0 @@
|
||||
.. highlight:: kotlin
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/codesets.js"></script>
|
||||
|
||||
Writing the contract
|
||||
====================
|
||||
|
||||
In Corda, the ledger is updated via transactions. Each transaction is a proposal to mark zero or more existing
|
||||
states as historic (the inputs), while creating zero or more new states (the outputs).
|
||||
|
||||
It's easy to imagine that most CorDapps will want to impose some constraints on how their states evolve over time:
|
||||
|
||||
* A cash CorDapp would not want to allow users to create transactions that generate money out of thin air (at least
|
||||
without the involvement of a central bank or commercial bank)
|
||||
* A loan CorDapp might not want to allow the creation of negative-valued loans
|
||||
* An asset-trading CorDapp would not want to allow users to finalise a trade without the agreement of their counterparty
|
||||
|
||||
In Corda, we impose constraints on what transactions are allowed using contracts. These contracts are very different
|
||||
to the smart contracts of other distributed ledger platforms. In Corda, contracts do not represent the current state of
|
||||
the ledger. Instead, like a real-world contract, they simply impose rules on what kinds of agreements are allowed.
|
||||
|
||||
Every state is associated with a contract. A transaction is invalid if it does not satisfy the contract of every
|
||||
input and output state in the transaction.
|
||||
|
||||
The Contract interface
|
||||
----------------------
|
||||
Just as every Corda state must implement the ``ContractState`` interface, every contract must implement the
|
||||
``Contract`` interface:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
|
||||
interface Contract {
|
||||
// Implements the contract constraints in code.
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun verify(tx: LedgerTransaction)
|
||||
}
|
||||
|
||||
You can read about function declarations in Kotlin `here <https://kotlinlang.org/docs/reference/functions.html>`_.
|
||||
|
||||
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input,
|
||||
and:
|
||||
|
||||
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
|
||||
* Returns silently if it accepts the transaction proposal
|
||||
|
||||
Controlling IOU evolution
|
||||
-------------------------
|
||||
What would a good contract for an ``IOUState`` look like? There is no right or wrong answer - it depends on how you
|
||||
want your CorDapp to behave.
|
||||
|
||||
For our CorDapp, let's impose the constraint that we only want to allow the creation of IOUs. We don't want nodes to
|
||||
transfer them or redeem them for cash. One way to enforce this behaviour would be by imposing the following constraints:
|
||||
|
||||
* A transaction involving IOUs must consume zero inputs, and create one output of type ``IOUState``
|
||||
* The transaction should also include a ``Create`` command, indicating the transaction's intent (more on commands
|
||||
shortly)
|
||||
* For the transactions's output IOU state:
|
||||
|
||||
* Its value must be non-negative
|
||||
* The lender and the borrower cannot be the same entity
|
||||
* The IOU's lender must sign the transaction
|
||||
|
||||
We can picture this transaction as follows:
|
||||
|
||||
.. image:: resources/simple-tutorial-transaction.png
|
||||
:scale: 15%
|
||||
:align: center
|
||||
|
||||
Defining IOUContract
|
||||
--------------------
|
||||
|
||||
Let's write a contract that enforces these constraints. We'll do this by modifying either ``TemplateContract.java`` or
|
||||
``App.kt`` and updating ``TemplateContract`` to define an ``IOUContract``:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/tutorial/helloworld/contract.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/helloworld/IOUContract.java
|
||||
:language: java
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.
|
||||
|
||||
Let's walk through this code step by step.
|
||||
|
||||
The Create command
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
The first thing we add to our contract is a *command*. Commands serve two functions:
|
||||
|
||||
* They indicate the transaction's intent, allowing us to perform different verification given the situation. For
|
||||
example, a transaction proposing the creation of an IOU could have to satisfy different constraints to one redeeming
|
||||
an IOU
|
||||
* They allow us to define the required signers for the transaction. For example, IOU creation might require signatures
|
||||
from the lender only, whereas the transfer of an IOU might require signatures from both the IOU's borrower and lender
|
||||
|
||||
Our contract has one command, a ``Create`` command. All commands must implement the ``CommandData`` interface.
|
||||
|
||||
The ``CommandData`` interface is a simple marker interface for commands. In fact, its declaration is only two words
|
||||
long (Kotlin interfaces do not require a body):
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. code-block:: kotlin
|
||||
|
||||
interface CommandData
|
||||
|
||||
The verify logic
|
||||
^^^^^^^^^^^^^^^^
|
||||
Our contract also needs to define the actual contract constraints. For our IOU CorDapp, we won't concern ourselves with
|
||||
writing valid legal prose to enforce the IOU agreement in court. Instead, we'll focus on implementing ``verify``.
|
||||
|
||||
Remember that our goal in writing the ``verify`` function is to write a function that, given a transaction:
|
||||
|
||||
* Throws an ``IllegalArgumentException`` if the transaction is considered invalid
|
||||
* Does **not** throw an exception if the transaction is considered valid
|
||||
|
||||
In deciding whether the transaction is valid, the ``verify`` function only has access to the contents of the
|
||||
transaction:
|
||||
|
||||
* ``tx.inputs``, which lists the inputs
|
||||
* ``tx.outputs``, which lists the outputs
|
||||
* ``tx.commands``, which lists the commands and their associated signers
|
||||
|
||||
Although we won't use them here, the ``verify`` function also has access to the transaction's attachments,
|
||||
time-windows, notary and hash.
|
||||
|
||||
Based on the constraints enumerated above, we'll write a ``verify`` function that rejects a transaction if any of the
|
||||
following are true:
|
||||
|
||||
* The transaction doesn't include a ``Create`` command
|
||||
* The transaction has inputs
|
||||
* The transaction doesn't have exactly one output
|
||||
* The IOU itself is invalid
|
||||
* The transaction doesn't require the lender's signature
|
||||
|
||||
Command constraints
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Our first constraint is around the transaction's commands. We use Corda's ``requireSingleCommand`` function to test for
|
||||
the presence of a single ``Create`` command. Here, ``requireSingleCommand`` performing a dual purpose:
|
||||
|
||||
* Asserting that there is exactly one ``Create`` command in the transaction
|
||||
* Extracting the command and returning it
|
||||
|
||||
If the ``Create`` command isn't present, or if the transaction has multiple ``Create`` commands, contract
|
||||
verification will fail.
|
||||
|
||||
Transaction constraints
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
We also want our transaction to have no inputs and only a single output - an issuance transaction.
|
||||
|
||||
To impose this and the subsequent constraints, we are using Corda's built-in ``requireThat`` function. ``requireThat``
|
||||
provides a terse way to write the following:
|
||||
|
||||
* If the condition on the right-hand side doesn't evaluate to true...
|
||||
* ...throw an ``IllegalArgumentException`` with the message on the left-hand side
|
||||
|
||||
As before, the act of throwing this exception would cause transaction verification to fail.
|
||||
|
||||
IOU constraints
|
||||
~~~~~~~~~~~~~~~
|
||||
We want to impose two constraints on the ``IOUState`` itself:
|
||||
|
||||
* Its value must be non-negative
|
||||
* The lender and the borrower cannot be the same entity
|
||||
|
||||
We impose these constraints in the same ``requireThat`` block as before.
|
||||
|
||||
You can see that we're not restricted to only writing constraints in the ``requireThat`` block. We can also write
|
||||
other statements - in this case, we're extracting the transaction's single ``IOUState`` and assigning it to a variable.
|
||||
|
||||
Signer constraints
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
Finally, we require the lender's signature on the transaction. A transaction's required signers is equal to the union
|
||||
of all the signers listed on the commands. We therefore extract the signers from the ``Create`` command we
|
||||
retrieved earlier.
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We've now written an ``IOUContract`` constraining the evolution of each ``IOUState`` over time:
|
||||
|
||||
* An ``IOUState`` can only be created, not transferred or redeemed
|
||||
* Creating an ``IOUState`` requires an issuance transaction with no inputs, a single ``IOUState`` output, and a
|
||||
``Create`` command
|
||||
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
|
||||
must be different entities
|
||||
|
||||
The final step in the creation of our CorDapp will be to write the ``IOUFlow`` that will allow a node to orchestrate
|
||||
the creation of a new ``IOUState`` on the ledger, while only sharing information on a need-to-know basis.
|
@ -6,35 +6,42 @@
|
||||
|
||||
Writing the flow
|
||||
================
|
||||
A flow describes the sequence of steps for agreeing a specific ledger update. By installing new flows on our node, we
|
||||
allow the node to handle new business processes. Our flow will allow a node to issue an ``IOUState`` onto the ledger.
|
||||
A flow encodes a sequence of steps that a node can run to achieve a specific ledger update. By installing new flows on
|
||||
a node, we allow the node to handle new business processes. The flow we define will allow a node to issue an
|
||||
``IOUState`` onto the ledger.
|
||||
|
||||
Flow outline
|
||||
------------
|
||||
Our flow needs to take the following steps for a borrower to issue a new IOU onto the ledger:
|
||||
The goal of our flow will be to orchestrate an IOU issuance transaction. Transactions in Corda are the atomic units of
|
||||
change that update the ledger. Each transaction is a proposal to mark zero or more existing states as historic (the
|
||||
inputs), while creating zero or more new states (the outputs).
|
||||
|
||||
1. Create a valid transaction proposal for the creation of a new IOU
|
||||
2. Verify the transaction
|
||||
3. Sign the transaction ourselves
|
||||
4. Record the transaction in our vault
|
||||
5. Send the transaction to the IOU's lender so that they can record it too
|
||||
The process of creating and applying this transaction to a ledger will be conducted by the IOU's lender, and will
|
||||
require the following steps:
|
||||
|
||||
1. Building the transaction proposal for the issuance of a new IOU onto a ledger
|
||||
2. Signing the transaction proposal
|
||||
3. Recording the transaction
|
||||
4. Sending the transaction to the IOU's borrower so that they can record it too
|
||||
|
||||
At this stage, we do not require the borrower to approve and sign IOU issuance transactions. We will be able to impose
|
||||
this requirement when we look at contracts in the next tutorial.
|
||||
|
||||
Subflows
|
||||
^^^^^^^^
|
||||
Although our flow requirements look complex, we can delegate to existing flows to handle many of these tasks. A flow
|
||||
that is invoked within the context of a larger flow to handle a repeatable task is called a *subflow*.
|
||||
Tasks like recording a transaction or sending a transaction to a counterparty are very common in Corda. Instead of
|
||||
forcing each developer to reimplement their own logic to handle these tasks, Corda provides a number of library flows
|
||||
to handle these tasks. We call these flows that are invoked in the context of a larger flow to handle a repeatable task
|
||||
*subflows*.
|
||||
|
||||
In our initiator flow, we can automate steps 4 and 5 using ``FinalityFlow``.
|
||||
|
||||
All we need to do is write the steps to handle the creation and signing of the proposed transaction.
|
||||
In our case, we can automate steps 3 and 4 of the IOU issuance flow using ``FinalityFlow``.
|
||||
|
||||
FlowLogic
|
||||
---------
|
||||
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
|
||||
``FlowLogic.call``.
|
||||
All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``.
|
||||
|
||||
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and
|
||||
replace them with the following:
|
||||
Let's define our ``IOUFlow`` in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the
|
||||
template, and replace them with the following:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -48,108 +55,105 @@ replace them with the following:
|
||||
:start-after: DOCSTART 01
|
||||
:end-before: DOCEND 01
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``.
|
||||
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``. Let's walk
|
||||
through this code step-by-step.
|
||||
|
||||
We now have our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. There's a few things to note:
|
||||
We've defined our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. ``FlowLogic.call`` has a return type
|
||||
that must match the type parameter passed to ``FlowLogic`` - this is type returned by running the flow.
|
||||
|
||||
* ``FlowLogic.call`` has a return type that matches the type parameter passed to ``FlowLogic`` - this is type returned
|
||||
by running the flow
|
||||
* ``FlowLogic`` subclasses can have constructor parameters, which can be used as arguments to ``FlowLogic.call``
|
||||
* ``FlowLogic.call`` is annotated ``@Suspendable`` - this means that the flow will be check-pointed and serialised to
|
||||
disk when it encounters a long-running operation, allowing your node to move on to running other flows. Forgetting
|
||||
this annotation out will lead to some very weird error messages
|
||||
* There are also a few more annotations, on the ``FlowLogic`` subclass itself:
|
||||
``FlowLogic`` subclasses can optionally have constructor parameters, which can be used as arguments to
|
||||
``FlowLogic.call``. In our case, we have two:
|
||||
|
||||
* ``iouValue``, which is the value of the IOU being issued
|
||||
* ``otherParty``, the IOU's borrower (the node running the flow is the lender)
|
||||
|
||||
``FlowLogic.call`` is annotated ``@Suspendable`` - this allows the flow to be check-pointed and serialised to disk when
|
||||
it encounters a long-running operation, allowing your node to move on to running other flows. Forgetting this
|
||||
annotation out will lead to some very weird error messages!
|
||||
|
||||
There are also a few more annotations, on the ``FlowLogic`` subclass itself:
|
||||
|
||||
* ``@InitiatingFlow`` means that this flow can be started directly by the node
|
||||
* ``@StartableByRPC`` allows the node owner to start this flow via an RPC call
|
||||
|
||||
* We override the progress tracker, even though we are not providing any progress tracker steps yet. The progress
|
||||
tracker is required for the node shell to establish when the flow has ended
|
||||
Let's walk through the steps of ``FlowLogic.call`` itself. This is where we actually describe the procedure for
|
||||
issuing the ``IOUState`` onto a ledger.
|
||||
|
||||
Let's walk through the steps of ``FlowLogic.call`` one-by-one:
|
||||
Choosing a notary
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Every transaction requires a notary to prevent double-spends and serve as a timestamping authority. The first thing we
|
||||
do in our flow is retrieve the a notary from the node's ``ServiceHub``. ``ServiceHub.networkMapCache`` provides
|
||||
information about the other nodes on the network and the services that they offer.
|
||||
|
||||
Retrieving participant information
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The identity of our counterparty is passed in as a constructor argument. However, we need to use the ``ServiceHub`` to
|
||||
retrieve our identity, as well as the identity of the notary we'll be using for our transaction.
|
||||
.. note::
|
||||
|
||||
You can see that the notary's identity is being retrieved from the node's ``ServiceHub``. Whenever we need
|
||||
information within a flow - whether it's about our own node, its contents, or the rest of the network - we use the
|
||||
node's ``ServiceHub``. In particular, ``ServiceHub.networkMapCache`` provides information about the other nodes on the
|
||||
network and the services that they offer.
|
||||
Whenever we need information within a flow - whether it's about our own node's identity, the node's local storage,
|
||||
or the rest of the network - we generally obtain it via the node's ``ServiceHub``.
|
||||
|
||||
Building the transaction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
We'll build our transaction proposal in two steps:
|
||||
|
||||
* Creating a transaction builder
|
||||
* Adding the desired items to the builder
|
||||
|
||||
Creating a transaction builder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
To start building the proposed transaction, we need a ``TransactionBuilder``. This is a mutable transaction class to
|
||||
which we can add inputs, outputs, commands, and any other items the transaction needs. We create a
|
||||
``TransactionBuilder`` that uses the notary we retrieved earlier.
|
||||
* Creating the transaction's components
|
||||
* Adding these components to a transaction builder
|
||||
|
||||
Transaction items
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Now that we have our ``TransactionBuilder``, we need to add the desired items. Remember that we're trying to build
|
||||
the following transaction:
|
||||
Our transaction will have the following structure:
|
||||
|
||||
.. image:: resources/simple-tutorial-transaction.png
|
||||
:scale: 15%
|
||||
:align: center
|
||||
|
||||
So we'll need the following:
|
||||
* The output ``IOUState`` on the right represents the state we will be adding to the ledger. As you can see, there are
|
||||
no inputs - we are not consuming any existing ledger states in the creation of our IOU
|
||||
|
||||
* The output ``IOUState`` and its associated contract
|
||||
* A ``Create`` command listing the IOU's lender as a signer
|
||||
* An ``Action`` command listing the IOU's lender as a signer
|
||||
|
||||
The command we use pairs the ``IOUContract.Create`` command defined earlier with our public key. Including this command
|
||||
in the transaction makes us one of the transaction's required signers.
|
||||
We've already talked about the ``IOUState``, but we haven't looked at commands yet. Commands serve two functions:
|
||||
|
||||
We add these items to the transaction using the ``TransactionBuilder.withItems`` method, which takes a ``vararg`` of:
|
||||
* They indicate the intent of a transaction - issuance, transfer, redemption, revocation. This will be crucial when we
|
||||
discuss contracts in the next tutorial
|
||||
* They allow us to define the required signers for the transaction. For example, IOU creation might require signatures
|
||||
from the lender only, whereas the transfer of an IOU might require signatures from both the IOU’s borrower and lender
|
||||
|
||||
* ``StateAndContract`` or ``TransactionState`` objects, which are added to the builder as output states
|
||||
* ``StateAndRef`` objects (references to the outputs of previous transactions), which are added to the builder as input
|
||||
state references
|
||||
* ``Command`` objects, which are added to the builder as commands
|
||||
* ``SecureHash`` objects, which are added to the builder as attachments
|
||||
* ``TimeWindow`` objects, which set the time-window of the transaction
|
||||
Each ``Command`` contains a command type plus a list of public keys. For now, we use the pre-defined
|
||||
``TemplateContract.Action`` as our command type, and we list the lender as the only public key. This means that for
|
||||
the transaction to be valid, the lender is required to sign the transaction.
|
||||
|
||||
It will modify the ``TransactionBuilder`` in-place to add these components to it.
|
||||
Creating a transaction builder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
To actually build the proposed transaction, we need a ``TransactionBuilder``. This is a mutable transaction class to
|
||||
which we can add inputs, outputs, commands, and any other items the transaction needs. We create a
|
||||
``TransactionBuilder`` that uses the notary we retrieved earlier.
|
||||
|
||||
Verifying the transaction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
We've now built our proposed transaction. Before we sign it, we should check that it represents a valid ledger update
|
||||
proposal by verifying the transaction, which will execute each of the transaction's contracts.
|
||||
Once we have the ``TransactionBuilder``, we add our components:
|
||||
|
||||
If the verification fails, we have built an invalid transaction. Our flow will then end, throwing a
|
||||
``TransactionVerificationException``.
|
||||
* The command is added directly using ``TransactionBuilder.addCommand``
|
||||
* The output ``IOUState`` is added using ``TransactionBuilder.addOutputState``. As well as the output state itself,
|
||||
this method takes a reference to the contract that will govern the evolution of the state over time. Here, we are
|
||||
passing in a reference to the ``TemplateContract``, which imposes no constraints. We will define a contract imposing
|
||||
real constraints in the next tutorial
|
||||
|
||||
Signing the transaction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Now that we have a valid transaction proposal, we need to sign it. Once the transaction is signed, no-one will be able
|
||||
to modify the transaction without invalidating our signature, effectively making the transaction immutable.
|
||||
to modify the transaction without invalidating this signature. This effectively makes the transaction immutable.
|
||||
|
||||
The call to ``ServiceHub.toSignedTransaction`` returns a ``SignedTransaction`` - an object that pairs the
|
||||
transaction itself with a list of signatures over that transaction.
|
||||
We sign the transaction using ``ServiceHub.toSignedTransaction``, which returns a ``SignedTransaction``. A
|
||||
``SignedTransaction`` is an object that pairs a transaction with a list of signatures over that transaction.
|
||||
|
||||
Finalising the transaction
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Now that we have a valid signed transaction, all that's left to do is to have it notarised and recorded by all the
|
||||
relevant parties. By doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process
|
||||
automatically using a built-in flow called ``FinalityFlow``:
|
||||
|
||||
``FinalityFlow`` completely automates the process of:
|
||||
We now have a valid signed transaction. All that's left to do is to have it recorded by all the relevant parties. By
|
||||
doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process automatically using a
|
||||
built-in flow called ``FinalityFlow``. ``FinalityFlow`` completely automates the process of:
|
||||
|
||||
* Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window)
|
||||
* Recording it in our vault
|
||||
* Sending it to the other participants (i.e. the lender) for them to record as well
|
||||
|
||||
Our flow, and our CorDapp, are now ready!
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We have now defined a flow that we can start on our node to completely automate the process of issuing an IOU onto the
|
||||
ledger. The final step is to spin up some nodes and test our CorDapp.
|
||||
Our flow, and our CorDapp, are now ready! We have now defined a flow that we can start on our node to completely
|
||||
automate the process of issuing an IOU onto the ledger. All that's left is to spin up some nodes and test our CorDapp.
|
@ -1,12 +0,0 @@
|
||||
Hello, World!
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
hello-world-introduction
|
||||
hello-world-template
|
||||
hello-world-state
|
||||
hello-world-contract
|
||||
hello-world-flow
|
||||
hello-world-running
|
@ -1,63 +1,62 @@
|
||||
Introduction
|
||||
============
|
||||
Hello, World!
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
hello-world-template
|
||||
hello-world-state
|
||||
hello-world-flow
|
||||
hello-world-running
|
||||
|
||||
By this point, :doc:`your dev environment should be set up <getting-set-up>`, you've run
|
||||
:doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What
|
||||
comes next?
|
||||
|
||||
If you're a developer, the next step is to write your own CorDapp. Each CorDapp takes the form of a JAR that is
|
||||
installed on one or more Corda nodes, and gives them the ability to conduct some new process - anything from
|
||||
If you're a developer, the next step is to write your own CorDapp. CorDapps are plugins that are installed on one or
|
||||
more Corda nodes, and give the nodes' owners the ability to make their node conduct some new process - anything from
|
||||
issuing a debt instrument to making a restaurant booking.
|
||||
|
||||
Our use-case
|
||||
------------
|
||||
Our CorDapp will seek to model IOUs on ledger. An IOU – short for “I Owe yoU” – records the fact that one person owes
|
||||
another a given amount of money. We can imagine that this is potentially sensitive information that we'd only want to
|
||||
communicate on a need-to-know basis. This is one of the areas where Corda excels - allowing a small set of parties to
|
||||
agree on a fact without needing to share this fact with everyone else on the network, as you do with most other
|
||||
blockchain platforms.
|
||||
Our CorDapp will model IOUs on-ledger. An IOU – short for “I O(we) (yo)U” – records the fact that one person owes
|
||||
another person a given amount of money. Clearly this is sensitive information that we'd only want to communicate on
|
||||
a need-to-know basis between the lender and the borrower. Fortunately, this is one of the areas where Corda excels.
|
||||
Corda makes it easy to allow a small set of parties to agree on a shared fact without needing to share this fact with
|
||||
everyone else on the network, as is the norm in blockchain platforms.
|
||||
|
||||
To serve any useful function, a CorDapp needs three core elements:
|
||||
To serve any useful function, our CorDapp will need at least two things:
|
||||
|
||||
* **One or more states** – the shared facts that will be agreed upon and stored on the ledger
|
||||
* **One or more contracts** – the rules governing how these states can evolve over time
|
||||
* **One or more flows** – the step-by-step process for carrying out a ledger update
|
||||
* **States**, the shared facts that Corda nodes reach consensus over and are then stored on the ledger
|
||||
* **Flows**, which encapsulate the procedure for carrying out a specific ledger update
|
||||
|
||||
Our IOU CorDapp is no exception. It will have the following elements:
|
||||
Our IOU CorDapp is no exception. It will define both a state and a flow:
|
||||
|
||||
State
|
||||
^^^^^
|
||||
The states will be IOUStates, with each instance representing a single IOU. We can visualize an IOUState as follows:
|
||||
The IOUState
|
||||
^^^^^^^^^^^^
|
||||
Our state will be the ``IOUState``. It will store the value of the IOU, as well as the identities of the lender and the
|
||||
borrower. We can visualize ``IOUState`` as follows:
|
||||
|
||||
.. image:: resources/tutorial-state.png
|
||||
:scale: 25%
|
||||
:align: center
|
||||
|
||||
Contract
|
||||
^^^^^^^^
|
||||
Our contract will be the IOUContract, imposing rules on the evolution of IOUs over time:
|
||||
|
||||
* Only the creation of new IOUs will be allowed
|
||||
* Transferring existing IOUs or paying off an IOU with cash will not be allowed
|
||||
|
||||
However, we can easily extend our CorDapp to handle additional use-cases later on.
|
||||
|
||||
Flow
|
||||
^^^^
|
||||
Our flow will be the IOUFlow. It will allow a node to orchestrate the creation of a new IOU on the ledger, via the
|
||||
following steps:
|
||||
The IOUFlow
|
||||
^^^^^^^^^^^
|
||||
Our flow will be the ``IOUFlow``. This flow will completely automate the process of issuing a new IOU onto a ledger. It
|
||||
is composed of the following steps:
|
||||
|
||||
.. image:: resources/simple-tutorial-flow.png
|
||||
:scale: 25%
|
||||
:align: center
|
||||
|
||||
In traditional distributed ledger systems, where all data is broadcast to every network participant, you don’t even
|
||||
think about this step – you simply package up your ledger update and send it out into the world. But in Corda, where
|
||||
privacy is a core focus, flows are used to carefully control who sees what during the process of agreeing a
|
||||
ledger update.
|
||||
In traditional distributed ledger systems, where all data is broadcast to every network participant, you don’t need to
|
||||
think about data flows – you simply package up your ledger update and send it to everyone else on the network. But in
|
||||
Corda, where privacy is a core focus, flows allow us to carefully control who sees what during the process of
|
||||
agreeing a ledger update.
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We've sketched out a simple CorDapp that will allow nodes to confidentially agree the creation of new IOUs.
|
||||
We've sketched out a simple CorDapp that will allow nodes to confidentially issue new IOUs onto a ledger.
|
||||
|
||||
Next, we'll be taking a look at the template project we'll be using as a base for our work.
|
||||
Next, we'll be taking a look at the template project we'll be using as the basis for our CorDapp.
|
@ -9,15 +9,12 @@ Running our CorDapp
|
||||
|
||||
Now that we've written a CorDapp, it's time to test it by running it on some real Corda nodes.
|
||||
|
||||
Clean up
|
||||
--------
|
||||
Before running our node, delete the ``client/TemplateClient.java`` (for Java) or ``client/TemplateClient.kt`` (for
|
||||
Kotlin) file. We won't be using it, and it will cause build errors unless we remove it.
|
||||
|
||||
Deploying our CorDapp
|
||||
---------------------
|
||||
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
|
||||
``task deployNodes`` section. This section defines three nodes - the Controller, PartyA, and PartyB:
|
||||
``task deployNodes`` section. This section defines three nodes. There are two standard nodes (``PartyA`` and
|
||||
``PartyB``), plus a special Controller node that is running the network map service and advertises a validating notary
|
||||
service.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@ -48,10 +45,6 @@ Let's take a look at the nodes we're going to deploy. Open the project's ``build
|
||||
}
|
||||
}
|
||||
|
||||
We have three standard nodes, plus a special Controller node that is running the network map service, and is also
|
||||
advertising a validating notary service. Feel free to add additional node definitions here to expand the size of the
|
||||
test network.
|
||||
|
||||
We can run this ``deployNodes`` task using Gradle. For each node definition, Gradle will:
|
||||
|
||||
* Package the project's source files into a CorDapp jar
|
||||
@ -114,11 +107,12 @@ We want to create an IOU of 100 with PartyB. We start the ``IOUFlow`` by typing:
|
||||
|
||||
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US"
|
||||
|
||||
PartyA and PartyB will automatically agree an IOU. If the flow worked, it should have led to the recording of a new IOU
|
||||
in the vaults of both PartyA and PartyB.
|
||||
This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
|
||||
the flow framework - it allows you to reduce complex negotiation and update processes into a single function call.
|
||||
|
||||
We can check the flow has worked by using an RPC operation to check the contents of each node's vault. Typing ``run``
|
||||
will display a list of the available commands. We can examine the contents of a node's vault by running:
|
||||
If the flow worked, it should have recorded a new IOU in the vaults of both PartyA and PartyB. Let's check.
|
||||
|
||||
We can check the contents of each node's vault by running:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -166,31 +160,23 @@ The vaults of PartyA and PartyB should both display the following output:
|
||||
stateTypes: "UNCONSUMED"
|
||||
otherResults: []
|
||||
|
||||
This is the transaction issuing our ``IOUState`` onto a ledger.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our
|
||||
CorDapp is made up of three key parts:
|
||||
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
|
||||
parts:
|
||||
|
||||
* The ``IOUState``, representing IOUs on the ledger
|
||||
* The ``IOUContract``, controlling the evolution of IOUs over time
|
||||
* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
|
||||
|
||||
Together, these three parts completely determine how IOUs are created and evolved on the ledger.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
There are a number of improvements we could make to this CorDapp:
|
||||
|
||||
* We could require signatures from the lender as well the borrower, to give both parties a say in the creation of a new
|
||||
``IOUState``
|
||||
* We should add unit tests, using the contract-test and flow-test frameworks
|
||||
* We should change ``IOUState.value`` from an integer to a proper amount of a given currency
|
||||
* We chould add unit tests, using the contract-test and flow-test frameworks
|
||||
* We chould change ``IOUState.value`` from an integer to a proper amount of a given currency
|
||||
* We could add an API, to make it easier to interact with the CorDapp
|
||||
|
||||
We will explore some of these improvements in future tutorials. But you should now be ready to develop your own
|
||||
CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_.
|
||||
|
||||
As you write CorDapps, you can learn more about the Corda API :doc:`here <corda-api>`.
|
||||
|
||||
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_,
|
||||
`Discourse <https://discourse.corda.net/>`_, or `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.
|
||||
But for now, the biggest priority is to add an ``IOUContract`` imposing constraints on the evolution of each
|
||||
``IOUState`` over time. This will be the focus of our next tutorial.
|
||||
|
@ -12,8 +12,8 @@ represent an IOU.
|
||||
|
||||
The ContractState interface
|
||||
---------------------------
|
||||
In Corda, any JVM class that implements the ``ContractState`` interface is a valid state. ``ContractState`` is
|
||||
defined as follows:
|
||||
A Corda state is any instance of a class that implements the ``ContractState`` interface. The ``ContractState``
|
||||
interface is defined as follows:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -24,6 +24,7 @@ defined as follows:
|
||||
val participants: List<AbstractParty>
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
The first thing you'll probably notice about this interface declaration is that its not written in Java or another
|
||||
common language. The core Corda platform, including the interface declaration above, is entirely written in Kotlin.
|
||||
|
||||
@ -35,31 +36,42 @@ If you do want to dive into Kotlin, there's an official
|
||||
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
||||
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
|
||||
|
||||
We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of
|
||||
the entities for which this state is relevant.
|
||||
We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the
|
||||
entities for which this state is relevant.
|
||||
|
||||
Beyond this, our state is free to define any fields, methods, helpers or inner classes it requires to accurately
|
||||
represent a given class of shared facts on the ledger.
|
||||
represent a given type of shared fact on the ledger.
|
||||
|
||||
``ContractState`` also has several child interfaces that you may wish to implement depending on your state, such as
|
||||
``LinearState`` and ``OwnableState``. See :doc:`api-states` for more information.
|
||||
.. note::
|
||||
|
||||
The first thing you'll probably notice about the declaration of ``ContractState`` is that its not written in Java
|
||||
or another common language. The core Corda platform, including the interface declaration above, is entirely written
|
||||
in Kotlin.
|
||||
|
||||
Learning some Kotlin will be very useful for understanding how Corda works internally, and usually only takes an
|
||||
experienced Java developer a day or so to pick up. However, learning Kotlin isn't essential. Because Kotlin code
|
||||
compiles to JVM bytecode, CorDapps written in other JVM languages such as Java can interoperate with Corda.
|
||||
|
||||
If you do want to dive into Kotlin, there's an official
|
||||
`getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
|
||||
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_.
|
||||
|
||||
Modelling IOUs
|
||||
--------------
|
||||
How should we define the ``IOUState`` representing IOUs on the ledger? Beyond implementing the ``ContractState``
|
||||
interface, our ``IOUState`` will also need properties to track the relevant features of the IOU:
|
||||
|
||||
* The value of the IOU
|
||||
* The lender of the IOU
|
||||
* The borrower of the IOU
|
||||
* The value of the IOU
|
||||
|
||||
There are many more fields you could include, such as the IOU's currency. We'll abstract them away for now. If
|
||||
you wish to add them later, its as simple as adding an additional property to your class definition.
|
||||
There are many more fields you could include, such as the IOU's currency, but let's ignore those for now. Adding them
|
||||
later is often as simple as adding an additional property to your class definition.
|
||||
|
||||
Defining IOUState
|
||||
-----------------
|
||||
Let's open ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and update ``TemplateState`` to
|
||||
define an ``IOUState``:
|
||||
Let's get started by opening ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and updating
|
||||
``TemplateState`` to define an ``IOUState``:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -75,23 +87,35 @@ define an ``IOUState``:
|
||||
|
||||
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``.
|
||||
|
||||
We've made the following changes:
|
||||
To define ``IOUState``, we've made the following changes:
|
||||
|
||||
* We've renamed ``TemplateState`` to ``IOUState``
|
||||
* We've added properties for ``value``, ``lender`` and ``borrower`` (along with any getters and setters in Java):
|
||||
* We've renamed the ``TemplateState`` class to ``IOUState``
|
||||
* We've added properties for ``value``, ``lender`` and ``borrower``, along with the required getters and setters in
|
||||
Java:
|
||||
|
||||
* ``value`` is just a standard int (in Java)/Int (in Kotlin)
|
||||
* ``lender`` and ``borrower`` are of type ``Party``. ``Party`` is a built-in Corda type that represents an entity on
|
||||
the network.
|
||||
* ``value`` is of type ``int`` (in Java)/``Int`` (in Kotlin)
|
||||
* ``lender`` and ``borrower`` are of type ``Party``
|
||||
|
||||
* ``Party`` is a built-in Corda type that represents an entity on the network
|
||||
|
||||
* We've overridden ``participants`` to return a list of the ``lender`` and ``borrower``
|
||||
|
||||
* Actions such as changing a state's contract or notary will require approval from all the ``participants``
|
||||
* ``participants`` is a list of all the parties who should be notified of the creation or consumption of this state
|
||||
|
||||
The IOUs that we issue onto a ledger will simply be instances of this class.
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on the ledger. As we've seen, states in
|
||||
Corda are simply JVM classes that implement the ``ContractState`` interface. They can have any additional properties and
|
||||
We've defined an ``IOUState`` that can be used to represent IOUs as shared facts on a ledger. As we've seen, states in
|
||||
Corda are simply classes that implement the ``ContractState`` interface. They can have any additional properties and
|
||||
methods you like.
|
||||
|
||||
Next, we'll be writing our ``IOUContract`` to control the evolution of these shared facts over time.
|
||||
All that's left to do is write the ``IOUFlow`` that will allow a node to orchestrate the creation of a new ``IOUState``
|
||||
on the ledger, while only sharing information on a need-to-know basis.
|
||||
|
||||
What about the contract?
|
||||
------------------------
|
||||
If you've read the white paper or Key Concepts section, you'll know that each state has an associated contract that
|
||||
imposes invariants on how the state evolves over time. Including a contract isn't crucial for our first CorDapp, so
|
||||
we'll just use the empty ``TemplateContract`` and ``TemplateContract.Commands.Action`` command defined by the template
|
||||
for now. In the next tutorial, we'll implement our own contract and command.
|
@ -7,35 +7,42 @@
|
||||
The CorDapp Template
|
||||
====================
|
||||
|
||||
When writing a new CorDapp, you’ll generally want to base it on the
|
||||
`Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_ or the equivalent
|
||||
`Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_. The Cordapp Template allows you to
|
||||
quickly deploy your CorDapp onto a local test network of dummy nodes to evaluate its functionality.
|
||||
When writing a new CorDapp, you’ll generally want to base it on the standard templates:
|
||||
|
||||
Note that there's no need to download and install Corda itself. As long as you're working from a stable Milestone
|
||||
branch, the required libraries will be downloaded automatically from an online repository.
|
||||
* The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_
|
||||
* The `Kotlin Cordapp Template <https://github.com/corda/cordapp-template-kotlin>`_
|
||||
|
||||
If you do wish to work from the latest snapshot, please follow the instructions
|
||||
`here <https://docs.corda.net/tutorial-cordapp.html#using-a-snapshot-release>`_.
|
||||
The Cordapp templates provide the required boilerplate for developing a CorDapp, and allow you to quickly deploy your
|
||||
CorDapp onto a local test network of dummy nodes to test its functionality.
|
||||
|
||||
CorDapps can be written in both Java and Kotlin, and will be providing the code in both languages in this tutorial.
|
||||
|
||||
Note that there's no need to download and install Corda itself. Corda V1.0's required libraries will be downloaded
|
||||
automatically from an online Maven repository.
|
||||
|
||||
Downloading the template
|
||||
------------------------
|
||||
Open a terminal window in the directory where you want to download the CorDapp template, and run the following commands:
|
||||
To download the template, open a terminal window in the directory where you want to download the CorDapp template, and
|
||||
run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Clone the template from GitHub:
|
||||
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
|
||||
|
||||
*or*
|
||||
|
||||
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
|
||||
|
||||
Opening the template in IntelliJ
|
||||
--------------------------------
|
||||
|
||||
Once the template is download, open it in IntelliJ by following the instructions here:
|
||||
https://docs.corda.net/tutorial-cordapp.html#opening-the-example-cordapp-in-intellij.
|
||||
|
||||
Template structure
|
||||
------------------
|
||||
We can write our CorDapp in either Java or Kotlin, and will be providing the code in both languages throughout. To
|
||||
implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin, we'll simply be modifying the
|
||||
``App.kt`` file:
|
||||
The template has a number of files, but we can ignore most of them. To implement our IOU CorDapp in Java, we'll only
|
||||
need to modify two files. For Kotlin, we'll simply be modifying the ``App.kt`` file:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
@ -44,23 +51,25 @@ implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin,
|
||||
// 1. The state
|
||||
src/main/java/com/template/TemplateState.java
|
||||
|
||||
// 2. The contract
|
||||
src/main/java/com/template/TemplateContract.java
|
||||
|
||||
// 3. The flow
|
||||
// 2. The flow
|
||||
src/main/java/com/template/TemplateFlow.java
|
||||
|
||||
.. code-block:: kotlin
|
||||
|
||||
src/main/kotlin/com/template/App.kt
|
||||
|
||||
To prevent build errors later on, you should delete the following file:
|
||||
Clean up
|
||||
--------
|
||||
To prevent build errors later on, we should delete the following files before we begin:
|
||||
|
||||
* Java: ``src/test/java/com/template/FlowTests.java``
|
||||
* Kotlin: ``src/test/kotlin/com/template/FlowTests.kt``
|
||||
* Java:
|
||||
* ``src/main/java/com/template/TemplateClient.java``
|
||||
* ``src/test/java/com/template/FlowTests.java``
|
||||
|
||||
* Kotlin:
|
||||
* ``src/main/kotlin/com/template/TemplateClient.kt``
|
||||
* ``src/test/kotlin/com/template/FlowTests.kt``
|
||||
|
||||
Progress so far
|
||||
---------------
|
||||
We now have a template that we can build upon to define our IOU CorDapp.
|
||||
|
||||
We'll begin writing the CorDapp proper by writing the definition of the ``IOUState``.
|
||||
We now have a template that we can build upon to define our IOU CorDapp. Let's start by defining the ``IOUState``.
|
||||
|
@ -14,7 +14,7 @@ Want to see Corda running? Download our demonstration application `DemoBench <ht
|
||||
follow our :doc:`quickstart guide </quickstart-index>`.
|
||||
|
||||
If you want to start coding on Corda, then familiarise yourself with the :doc:`key concepts </key-concepts>`, then read
|
||||
our :doc:`Hello, World! tutorial </hello-world-index>`. For the background behind Corda, read the non-technical
|
||||
our :doc:`Hello, World! tutorial </hello-world-introduction>`. For the background behind Corda, read the non-technical
|
||||
`introductory white paper`_ or for more detail, the `technical white paper`_.
|
||||
|
||||
If you have questions or comments, then get in touch on `Slack <https://slack.corda.net/>`_ or write a question on
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user