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:
josecoll 2017-11-17 15:11:24 +00:00
commit 7a9364c8d5
299 changed files with 6187 additions and 3859 deletions

View File

@ -53,6 +53,154 @@ public interface net.corda.core.concurrent.CordaFuture extends java.util.concurr
public abstract void then(kotlin.jvm.functions.Function1) public abstract void then(kotlin.jvm.functions.Function1)
@org.jetbrains.annotations.NotNull public abstract concurrent.CompletableFuture toCompletableFuture() @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 @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 boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
public static final net.corda.core.contracts.AlwaysAcceptAttachmentConstraint INSTANCE 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 int hashCode()
public String toString() 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) 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 net.corda.core.contracts.StateRef component1()
@org.jetbrains.annotations.NotNull public final java.time.Instant component2() @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 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) @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 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 getMidpoint()
@org.jetbrains.annotations.Nullable public abstract java.time.Instant getUntilTime() @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) @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 @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) 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 @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) 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 isValid(byte[])
public final boolean verify(net.corda.core.utilities.OpaqueBytes) public final boolean verify(net.corda.core.utilities.OpaqueBytes)
public final boolean verify(byte[]) 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 public abstract class net.corda.core.crypto.MerkleTree extends java.lang.Object
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash getHash() @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 int hashCode()
public String toString() 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 @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() @org.jetbrains.annotations.NotNull public String getName()
public static final net.corda.core.flows.FlowInitiator$Shell INSTANCE 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() @co.paralleluniverse.fibers.Suspendable public abstract Object call()
public final void checkFlowPermission(String, Map) public final void checkFlowPermission(String, Map)
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.Nullable public final net.corda.core.flows.FlowStackSnapshot flowStackSnapshot() @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) @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 org.slf4j.Logger getLogger()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getOurIdentity() @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 @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() @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) @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) 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 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) @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) 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) @co.paralleluniverse.fibers.Suspendable public Object subFlow(net.corda.core.flows.FlowLogic)
@org.jetbrains.annotations.Nullable public final net.corda.core.messaging.DataFeed track() @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)
@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 @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 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)
public <init>(net.corda.core.flows.FlowSession, boolean) 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() @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public net.corda.core.transactions.SignedTransaction call()
## ##
public @interface net.corda.core.flows.SchedulableFlow 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.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 @net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.StateMachineRunId extends java.lang.Object
public <init>(UUID) public <init>(UUID)
@org.jetbrains.annotations.NotNull public final UUID component1() @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 List networkMapSnapshot()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NodeInfo nodeInfo() @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) @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.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.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 java.io.InputStream openAttachment(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public abstract Set partiesFromName(String, boolean) @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.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() @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() @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() @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() @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 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 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) @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) @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) @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 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 @net.corda.core.DoNotImplement public interface net.corda.core.messaging.RPCOps
public abstract int getProtocolVersion() 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 @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)
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 net.corda.core.flows.StateMachineRunId component1()
@org.jetbrains.annotations.NotNull public final String component2() @org.jetbrains.annotations.NotNull public final String component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator component3() @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.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)
@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) 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 String getFlowLogicClassName()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateMachineRunId getId() @org.jetbrains.annotations.NotNull public final net.corda.core.flows.StateMachineRunId getId()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.FlowInitiator getInitiator() @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 int getPlatformVersion()
public final long getSerial() public final long getSerial()
public int hashCode() 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 final boolean isLegalIdentity(net.corda.core.identity.Party)
public String toString() 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.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.services.KeyManagementService getKeyManagementService()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NodeInfo getMyInfo() @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.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.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.TransactionStorage getValidatedTransactions()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.VaultService getVaultService() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.VaultService getVaultService()
@org.jetbrains.annotations.NotNull public abstract java.sql.Connection jdbcSession() @org.jetbrains.annotations.NotNull public abstract java.sql.Connection jdbcSession()
public abstract void recordTransactions(Iterable) public abstract void recordTransactions(Iterable)
public abstract void recordTransactions(net.corda.core.node.StatesToRecord, Iterable)
public abstract void recordTransactions(boolean, 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)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.transactions.SignedTransaction signInitialTransaction(net.corda.core.transactions.TransactionBuilder, Iterable) @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 @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) @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 @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)
@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.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 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() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.ServiceHub getServices()
public abstract void start() public abstract void start()
public abstract void stop() 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 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() @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 valueOf(String)
public static net.corda.core.node.services.vault.AggregateFunctionType[] values() 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 @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) protected <init>(String, int)
public static net.corda.core.node.services.vault.BinaryComparisonOperator valueOf(String) 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) @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.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.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) @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.CriteriaExpression$ColumnPredicateExpression isNull(kotlin.reflect.KProperty1)
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.ColumnPredicate$BinaryComparison lessThan(Comparable) @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) @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) @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.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) @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) @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) @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) @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.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.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) @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) @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) @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 valueOf(String)
public static net.corda.core.node.services.vault.EqualityComparisonOperator[] values() public static net.corda.core.node.services.vault.EqualityComparisonOperator[] values()
## ##
@net.corda.core.DoNotImplement public interface net.corda.core.node.services.vault.IQueryCriteriaParser public interface net.corda.core.node.services.vault.GenericQueryCriteria
@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 visit(net.corda.core.node.services.vault.BaseQueryCriteriaParser)
@org.jetbrains.annotations.NotNull public abstract Collection parseAnd(net.corda.core.node.services.vault.QueryCriteria, net.corda.core.node.services.vault.QueryCriteria) ##
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$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$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$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$VaultCustomQueryCriteria)
@org.jetbrains.annotations.NotNull public abstract Collection parseCriteria(net.corda.core.node.services.vault.QueryCriteria$VaultQueryCriteria) @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 @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) 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 final boolean isDefault()
public String toString() public String toString()
## ##
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.node.services.vault.QueryCriteria extends java.lang.Object @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 final 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 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 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 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 @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>() public <init>()
@ -2151,6 +2446,12 @@ public final class net.corda.core.node.services.VaultServiceKt extends java.lang
public String toString() public String toString()
@org.jetbrains.annotations.NotNull public Collection visit(net.corda.core.node.services.vault.IQueryCriteriaParser) @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 @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) 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() @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 DEFAULT_PAGE_SIZE = 200
public static final int MAX_PAGE_SIZE = 2147483647 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) public <init>(Collection)
@org.jetbrains.annotations.NotNull public final Collection component1() @org.jetbrains.annotations.NotNull public final Collection component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.vault.Sort copy(Collection) @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 valueOf(String)
public static net.corda.core.serialization.SerializationContext$UseCase[] values() 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 public final class net.corda.core.serialization.SerializationDefaults extends java.lang.Object
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT() @org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializationContext getCHECKPOINT_CONTEXT()
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getP2P_CONTEXT() @org.jetbrains.annotations.NotNull public final 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 final 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 final 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 final net.corda.core.serialization.SerializationFactory getSERIALIZATION_FACTORY()
@org.jetbrains.annotations.NotNull public net.corda.core.serialization.SerializationContext getSTORAGE_CONTEXT() @org.jetbrains.annotations.NotNull public final 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 static final net.corda.core.serialization.SerializationDefaults INSTANCE public static final net.corda.core.serialization.SerializationDefaults INSTANCE
## ##
public abstract class net.corda.core.serialization.SerializationFactory extends java.lang.Object 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() 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 @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) @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 checkAllComponentsVisible(net.corda.core.contracts.ComponentGroupEnum)
public final void checkCommandVisibility(java.security.PublicKey)
public final boolean checkWithFun(kotlin.jvm.functions.Function1) public final boolean checkWithFun(kotlin.jvm.functions.Function1)
@org.jetbrains.annotations.NotNull public final List getFilteredComponentGroups() @org.jetbrains.annotations.NotNull public final List getFilteredComponentGroups()
@org.jetbrains.annotations.NotNull public final List getGroupHashes() @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) @org.jetbrains.annotations.NotNull public final List inputsOfType(Class)
public String toString() public String toString()
public final void verify() 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 static final class net.corda.core.transactions.LedgerTransaction$InOutGroup extends java.lang.Object
public <init>(List, List, Object) public <init>(List, List, Object)
@ -2639,6 +2937,7 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
public int hashCode() public int hashCode()
public String toString() public String toString()
public void verifyRequiredSignatures() 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 @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) 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() @org.jetbrains.annotations.NotNull public List getOutputs()
public int hashCode() 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.ServiceHub, List)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeLedgerTransaction resolve(net.corda.core.node.StateLoader, List)
public String toString() 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 @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() 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(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.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.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)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction toLedgerTransaction(net.corda.core.node.ServiceHub, boolean) @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() @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)
public final void verify(net.corda.core.node.ServiceHub, boolean) public final void verify(net.corda.core.node.ServiceHub, boolean)
public void verifyRequiredSignatures() 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(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 withAdditionalSignature(net.corda.core.crypto.TransactionSignature)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction withAdditionalSignatures(Iterable) @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 Set getRequiredSigningKeys()
@org.jetbrains.annotations.NotNull public abstract List getSigs() @org.jetbrains.annotations.NotNull public abstract List getSigs()
public abstract void verifyRequiredSignatures() 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 @net.corda.core.DoNotImplement public abstract class net.corda.core.transactions.TraversableTransaction extends net.corda.core.transactions.CoreTransaction
public <init>(List) 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 toBase64(byte[])
@org.jetbrains.annotations.NotNull public static final String toHex(byte[]) @org.jetbrains.annotations.NotNull public static final String toHex(byte[])
@org.jetbrains.annotations.NotNull public static final byte[] toSHA256Bytes(java.security.PublicKey) @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 final class net.corda.core.utilities.KotlinUtilsKt extends java.lang.Object
public static final void debug(org.slf4j.Logger, kotlin.jvm.functions.Function0) 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 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 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 interface net.corda.core.utilities.VariablePropertyDelegate extends net.corda.core.utilities.PropertyDelegate
public abstract void setValue(Object, kotlin.reflect.KProperty, Object) 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)
public <init>(net.corda.core.utilities.NetworkHostAndPort, net.corda.client.rpc.CordaRPCClientConfiguration) 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)
@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 Object use(String, String, kotlin.jvm.functions.Function1)
## ##
public final class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.Object public final class net.corda.client.rpc.CordaRPCClientConfiguration extends java.lang.Object

18
.idea/compiler.xml generated
View File

@ -16,6 +16,9 @@
<module name="client_test" target="1.8" /> <module name="client_test" target="1.8" />
<module name="confidential-identities_main" target="1.8" /> <module name="confidential-identities_main" target="1.8" />
<module name="confidential-identities_test" 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_main" target="1.8" />
<module name="corda-project_test" target="1.8" /> <module name="corda-project_test" target="1.8" />
<module name="cordapp_integrationTest" 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_main" target="1.8" />
<module name="docs_source_example-code_test" target="1.8" /> <module name="docs_source_example-code_test" target="1.8" />
<module name="docs_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_main" target="1.8" />
<module name="experimental_test" target="1.8" /> <module name="experimental_test" target="1.8" />
<module name="explorer-capsule_main" target="1.6" /> <module name="explorer-capsule_main" target="1.6" />
@ -50,10 +60,15 @@
<module name="gradle-plugins-cordform-common_test" target="1.8" /> <module name="gradle-plugins-cordform-common_test" target="1.8" />
<module name="graphs_main" target="1.8" /> <module name="graphs_main" target="1.8" />
<module name="graphs_test" 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_main" target="1.8" />
<module name="intellij-plugin_test" 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" 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" 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_integrationTest" target="1.8" />
<module name="irs-demo_main" target="1.8" /> <module name="irs-demo_main" target="1.8" />
<module name="irs-demo_test" 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="simm-valuation-demo_test" target="1.8" />
<module name="smoke-test-utils_main" target="1.8" /> <module name="smoke-test-utils_main" target="1.8" />
<module name="smoke-test-utils_test" 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_main" target="1.8" />
<module name="test-common_test" target="1.8" /> <module name="test-common_test" target="1.8" />
<module name="test-utils_main" target="1.8" /> <module name="test-utils_main" target="1.8" />

View File

@ -1,7 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="BankOfCordaDriverKt - Issue Web" type="JetRunConfigurationType" factoryName="Kotlin"> <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="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="MAIN_CLASS_NAME" value="net.corda.bank.BankOfCordaDriverKt" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="--role ISSUE_CASH_WEB --quantity 100 --currency USD" /> <option name="PROGRAM_PARAMETERS" value="--role ISSUE_CASH_WEB --quantity 100 --currency USD" />

View File

@ -1,7 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="BankOfCordaDriverKt - Run Stack" type="JetRunConfigurationType" factoryName="Kotlin"> <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="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="MAIN_CLASS_NAME" value="net.corda.bank.BankOfCordaDriverKt" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="--role ISSUER" /> <option name="PROGRAM_PARAMETERS" value="--role ISSUER" />

View File

@ -1,7 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Explorer - demo nodes" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true"> <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="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="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />

View File

@ -1,7 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Explorer - demo nodes (simulation)" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true"> <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="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="MAIN_CLASS_NAME" value="net.corda.explorer.MainKt" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="-S" /> <option name="PROGRAM_PARAMETERS" value="-S" />

View File

@ -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 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) 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. 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 ## Useful links

View File

@ -21,7 +21,7 @@ buildscript {
ext.capsule_version = '1.0.1' ext.capsule_version = '1.0.1'
ext.asm_version = '0.5.3' 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.jackson_version = '2.9.2'
ext.jetty_version = '9.4.7.v20170914' ext.jetty_version = '9.4.7.v20170914'
ext.jersey_version = '2.25' ext.jersey_version = '2.25'

View File

@ -6,11 +6,11 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.context.Origin
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.StateMachineUpdate
@ -64,7 +64,7 @@ class NodeMonitorModelTest : IntegrationTest() {
invokeRpc(CordaRPCOps::stateMachinesFeed), invokeRpc(CordaRPCOps::stateMachinesFeed),
invokeRpc(CordaRPCOps::networkMapFeed)) 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 aliceNode = aliceNodeHandle.nodeInfo
newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo }
val monitor = NodeMonitorModel() val monitor = NodeMonitorModel()
@ -79,7 +79,7 @@ class NodeMonitorModelTest : IntegrationTest() {
rpc = monitor.proxyObservable.value!! rpc = monitor.proxyObservable.value!!
notaryParty = defaultNotaryIdentity 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 bobNode = bobNodeHandle.nodeInfo
val monitorBob = NodeMonitorModel() val monitorBob = NodeMonitorModel()
stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed() stateMachineUpdatesBob = monitorBob.stateMachineUpdates.bufferUntilSubscribed()
@ -91,20 +91,20 @@ class NodeMonitorModelTest : IntegrationTest() {
@Test @Test
fun `network map update`() = setup { fun `network map update`() = setup {
val charlieNode = newNode(CHARLIE.name) val charlieNode = newNode(CHARLIE_NAME)
val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts
networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } } networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } }
.expectEvents(isStrict = false) { .expectEvents(isStrict = false) {
sequence( sequence(
// TODO : Add test for remove when driver DSL support individual node shutdown. // TODO : Add test for remove when driver DSL support individual node shutdown.
expect { output: NetworkMapCache.MapChange -> 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 -> 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 -> 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 // ISSUE
expect { add: StateMachineUpdate.Added -> expect { add: StateMachineUpdate.Added ->
issueSmId = add.id issueSmId = add.id
val initiator = add.stateMachineInfo.initiator val context = add.stateMachineInfo.context()
require(initiator is FlowInitiator.RPC && initiator.username == "user1") require(context.origin is Origin.RPC && context.principal().name == "user1")
}, },
expect { remove: StateMachineUpdate.Removed -> expect { remove: StateMachineUpdate.Removed ->
require(remove.id == issueSmId) 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 // 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 -> expect(match = { it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added ->
moveSmId = add.id moveSmId = add.id
val initiator = add.stateMachineInfo.initiator val context = add.stateMachineInfo.context()
require(initiator is FlowInitiator.RPC && initiator.username == "user1") require(context.origin is Origin.RPC && context.principal().name == "user1")
}, },
expect(match = { it is StateMachineUpdate.Removed && it.id == moveSmId }) { expect(match = { it is StateMachineUpdate.Removed && it.id == moveSmId }) {
} }
@ -169,8 +169,8 @@ class NodeMonitorModelTest : IntegrationTest() {
sequence( sequence(
// MOVE // MOVE
expect { add: StateMachineUpdate.Added -> expect { add: StateMachineUpdate.Added ->
val initiator = add.stateMachineInfo.initiator val context = add.stateMachineInfo.context()
require(initiator is FlowInitiator.Peer && aliceNode.isLegalIdentity(initiator.party)) require(context.origin is Origin.Peer && aliceNode.isLegalIdentity(aliceNode.identityFromX500Name((context.origin as Origin.Peer).party)))
} }
) )
} }

View File

@ -1,7 +1,8 @@
package net.corda.client.rpc package net.corda.client.rpc
import net.corda.core.context.*
import net.corda.core.crypto.random63BitValue 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.concurrent.flatMap
import net.corda.core.internal.packageName import net.corda.core.internal.packageName
import net.corda.core.messaging.* 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.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.ALICE import net.corda.testing.*
import net.corda.testing.chooseIdentity
import net.corda.testing.internal.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -41,18 +42,20 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
invokeRpc("vaultQueryByCriteria")) invokeRpc("vaultQueryByCriteria"))
) )
private lateinit var node: StartedNode<Node> private lateinit var node: StartedNode<Node>
private lateinit var identity: Party
private lateinit var client: CordaRPCClient private lateinit var client: CordaRPCClient
private var connection: CordaRPCConnection? = null private var connection: CordaRPCConnection? = null
private fun login(username: String, password: String) { private fun login(username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null) {
connection = client.start(username, password) connection = client.start(username, password, externalTrace, impersonatedActor)
} }
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
node = startNode(ALICE.name, rpcUsers = listOf(rpcUser)) node = startNode(ALICE_NAME, rpcUsers = listOf(rpcUser))
client = CordaRPCClient(node.internals.configuration.rpcAddress!!) client = CordaRPCClient(node.internals.configuration.rpcAddress!!)
identity = node.info.identityFromX500Name(ALICE_NAME)
} }
@After @After
@ -86,7 +89,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
println("Creating proxy") println("Creating proxy")
println("Starting flow") println("Starting flow")
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow, 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") println("Started flow, waiting on result")
flowHandle.progress.subscribe { flowHandle.progress.subscribe {
@ -98,7 +101,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
@Test @Test
fun `sub-type of FlowException thrown by flow`() { fun `sub-type of FlowException thrown by flow`() {
login(rpcUser.username, rpcUser.password) 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 { assertThatExceptionOfType(CashException::class.java).isThrownBy {
handle.returnValue.getOrThrow() handle.returnValue.getOrThrow()
} }
@ -107,7 +110,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
@Test @Test
fun `check basic flow has no progress`() { fun `check basic flow has no progress`() {
login(rpcUser.username, rpcUser.password) 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<*>) 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") assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = proxy.startFlow(::CashIssueFlow, 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") println("Started issuing cash, waiting on result")
flowHandle.returnValue.get() flowHandle.returnValue.get()
@ -132,31 +135,50 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance.contracts", C
@Test @Test
fun `flow initiator via RPC`() { 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 val proxy = connection!!.proxy
var countRpcFlows = 0
var countShellFlows = 0 val updates = proxy.stateMachinesFeed().updates
proxy.stateMachinesFeed().updates.subscribe {
if (it is StateMachineUpdate.Added) { node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0),identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
val initiator = it.stateMachineInfo.initiator proxy.startFlow(::CashIssueFlow, 123.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
if (initiator is FlowInitiator.RPC) proxy.startFlowDynamic(CashIssueFlow::class.java, 1000.DOLLARS, OpaqueBytes.of(0), identity).returnValue.getOrThrow()
countRpcFlows++
if (initiator is FlowInitiator.Shell) val historicalIds = mutableSetOf<Trace.InvocationId>()
countShellFlows++ 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)
}

View File

@ -2,17 +2,22 @@ package net.corda.client.rpc
import net.corda.client.rpc.internal.RPCClient import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration import net.corda.client.rpc.internal.RPCClientConfiguration
import net.corda.core.context.Trace
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose 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.messaging.RPCOps
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.testing.* import net.corda.nodeapi.User
import net.corda.testing.driver.poll import net.corda.testing.driver.poll
import net.corda.testing.internal.*
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue 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 { interface TrackSubscriberOps : RPCOps {
fun subscribe(): Observable<Unit> fun subscribe(): Observable<Unit>
} }
@ -320,9 +349,10 @@ class RPCStabilityTests : IntegrationTest() {
val message = session.createMessage(false) val message = session.createMessage(false)
val request = RPCApi.ClientToServer.RpcRequest( val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue), clientAddress = SimpleString(myQueue),
id = RPCApi.RpcRequestId(random63BitValue()),
methodName = SlowConsumerRPCOps::streamAtInterval.name, 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) request.writeToClientMessage(message)
producer.send(message) producer.send(message)

View File

@ -3,11 +3,13 @@ package net.corda.client.rpc
import net.corda.client.rpc.internal.KryoClientSerializationScheme import net.corda.client.rpc.internal.KryoClientSerializationScheme
import net.corda.client.rpc.internal.RPCClient import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.RPCClientConfiguration 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.messaging.CordaRPCOps
import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection 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 net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
import java.time.Duration import java.time.Duration
@ -71,8 +73,15 @@ class CordaRPCClient @JvmOverloads constructor(
configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT configuration: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT
) { ) {
init { init {
// TODO: allow clients to have serialization factory etc injected and align with RPC protocol version? try {
KryoClientSerializationScheme.initialiseSerialization() 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>( 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. * @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 { fun start(username: String, password: String): CordaRPCConnection {
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password)) return start(username, password, null, null)
}
/**
* Logs in to the target server and returns an active connection. The returned connection is a [java.io.Closeable]
* and can be used with a try-with-resources statement. If you don't use that, you should use the
* [RPCConnection.notifyServerAndClose] or [RPCConnection.forceClose] methods to dispose of the connection object
* when done.
*
* @param username The username to authenticate with.
* @param password The password to authenticate with.
* @param externalTrace external [Trace] for correlation.
* @throws RPCException if the server version is too low or if the server isn't reachable within a reasonable timeout.
*/
fun start(username: String, password: String, externalTrace: Trace?, impersonatedActor: Actor?): CordaRPCConnection {
return CordaRPCConnection(rpcClient.start(CordaRPCOps::class.java, username, password, externalTrace, impersonatedActor))
} }
/** /**

View File

@ -2,15 +2,18 @@ package net.corda.client.rpc.internal
import com.esotericsoftware.kryo.pool.KryoPool import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.serialization.SerializationContext 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.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.amqp.AMQPClientSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer
import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1
import net.corda.nodeapi.internal.serialization.kryo.RPCKryo import net.corda.nodeapi.internal.serialization.kryo.RPCKryo
import java.util.concurrent.atomic.AtomicBoolean
class KryoClientSerializationScheme : AbstractKryoSerializationScheme() { class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean {
@ -19,7 +22,7 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
override fun rpcClientKryoPool(context: SerializationContext): KryoPool { override fun rpcClientKryoPool(context: SerializationContext): KryoPool {
return KryoPool.Builder { return KryoPool.Builder {
DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context)).apply { DefaultKryoCustomizer.customize(RPCKryo(RpcClientObservableSerializer, context), publicKeySerializer).apply {
classLoader = context.deserializationClassLoader classLoader = context.deserializationClassLoader
} }
}.build() }.build()
@ -29,25 +32,19 @@ class KryoClientSerializationScheme : AbstractKryoSerializationScheme() {
override fun rpcServerKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException() override fun rpcServerKryoPool(context: SerializationContext): KryoPool = throw UnsupportedOperationException()
companion object { companion object {
val isInitialised = AtomicBoolean(false) /** Call from main only. */
fun initialiseSerialization() { fun initialiseSerialization() {
if (!isInitialised.compareAndSet(false, true)) return nodeSerializationEnv = createSerializationEnv()
try { }
SerializationDefaults.SERIALIZATION_FACTORY = SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme()) fun createSerializationEnv(): SerializationEnvironment {
registerScheme(AMQPClientSerializationScheme()) return SerializationEnvironmentImpl(
} SerializationFactoryImpl().apply {
SerializationDefaults.P2P_CONTEXT = KRYO_P2P_CONTEXT registerScheme(KryoClientSerializationScheme())
SerializationDefaults.RPC_CLIENT_CONTEXT = KRYO_RPC_CLIENT_CONTEXT registerScheme(AMQPClientSerializationScheme())
} catch (e: IllegalStateException) { },
// Check that it's registered as we expect KRYO_P2P_CONTEXT,
val factory = SerializationDefaults.SERIALIZATION_FACTORY rpcClientContext = KRYO_RPC_CLIENT_CONTEXT)
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."
}
}
} }
} }
} }

View File

@ -2,12 +2,15 @@ package net.corda.client.rpc.internal
import net.corda.client.rpc.RPCConnection import net.corda.client.rpc.RPCConnection
import net.corda.client.rpc.RPCException 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.crypto.random63BitValue
import net.corda.core.internal.logElapsedTime import net.corda.core.internal.logElapsedTime
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
@ -99,7 +102,9 @@ class RPCClient<I : RPCOps>(
fun start( fun start(
rpcOpsClass: Class<I>, rpcOpsClass: Class<I>,
username: String, username: String,
password: String password: String,
externalTrace: Trace? = null,
impersonatedActor: Actor? = null
): RPCConnection<I> { ): RPCConnection<I> {
return log.logElapsedTime("Startup") { return log.logElapsedTime("Startup") {
val clientAddress = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.${random63BitValue()}") val clientAddress = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.${random63BitValue()}")
@ -110,9 +115,10 @@ class RPCClient<I : RPCOps>(
maxRetryInterval = rpcConfiguration.connectionMaxRetryInterval.toMillis() maxRetryInterval = rpcConfiguration.connectionMaxRetryInterval.toMillis()
reconnectAttempts = rpcConfiguration.maxReconnectAttempts reconnectAttempts = rpcConfiguration.maxReconnectAttempts
minLargeMessageSize = rpcConfiguration.maxFileSize minLargeMessageSize = rpcConfiguration.maxFileSize
isUseGlobalPools = nodeSerializationEnv != null
} }
val sessionId = Trace.SessionId.newInstance()
val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext) val proxyHandler = RPCClientProxyHandler(rpcConfiguration, username, password, serverLocator, clientAddress, rpcOpsClass, serializationContext, sessionId, externalTrace, impersonatedActor)
try { try {
proxyHandler.start() proxyHandler.start()
val ops: I = uncheckedCast(Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), proxyHandler)) val ops: I = uncheckedCast(Proxy.newProxyInstance(rpcOpsClass.classLoader, arrayOf(rpcOpsClass), proxyHandler))

View File

@ -12,7 +12,9 @@ import com.google.common.util.concurrent.SettableFuture
import com.google.common.util.concurrent.ThreadFactoryBuilder import com.google.common.util.concurrent.ThreadFactoryBuilder
import net.corda.client.rpc.RPCException import net.corda.client.rpc.RPCException
import net.corda.client.rpc.RPCSinceVersion 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.LazyPool
import net.corda.core.internal.LazyStickyPool import net.corda.core.internal.LazyStickyPool
import net.corda.core.internal.LifeCycle 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.debug
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor 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.config.ActiveMQDefaultConfiguration
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE 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 rx.subjects.UnicastSubject
import java.lang.reflect.InvocationHandler import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method import java.lang.reflect.Method
import java.time.Instant
import java.util.* import java.util.*
import java.util.concurrent.* import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@ -70,7 +75,10 @@ class RPCClientProxyHandler(
private val serverLocator: ServerLocator, private val serverLocator: ServerLocator,
private val clientAddress: SimpleString, private val clientAddress: SimpleString,
private val rpcOpsClass: Class<out RPCOps>, private val rpcOpsClass: Class<out RPCOps>,
serializationContext: SerializationContext serializationContext: SerializationContext,
private val sessionId: Trace.SessionId,
private val externalTrace: Trace?,
private val impersonatedActor: Actor?
) : InvocationHandler { ) : InvocationHandler {
private enum class State { 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. // Stores the Observable IDs that are already removed from the map but are not yet sent to the server.
private val observablesToReap = ThreadBox(object { private val observablesToReap = ThreadBox(object {
var observables = ArrayList<RPCApi.ObservableId>() var observables = ArrayList<InvocationId>()
}) })
private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext) private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext)
private fun createRpcObservableMap(): RpcObservableMap { private fun createRpcObservableMap(): RpcObservableMap {
val onObservableRemove = RemovalListener<RPCApi.ObservableId, UnicastSubject<Notification<*>>> { val onObservableRemove = RemovalListener<InvocationId, UnicastSubject<Notification<*>>> {
val observableId = it.key!! val observableId = it.key!!
val rpcCallSite = callSiteMap?.remove(observableId.toLong) val rpcCallSite = callSiteMap?.remove(observableId)
if (it.cause == RemovalCause.COLLECTED) { if (it.cause == RemovalCause.COLLECTED) {
log.warn(listOf( log.warn(listOf(
"A hot observable returned from an RPC was never subscribed to.", "A hot observable returned from an RPC was never subscribed to.",
@ -207,11 +215,12 @@ class RPCClientProxyHandler(
if (sessionAndConsumer!!.session.isClosed) { if (sessionAndConsumer!!.session.isClosed) {
throw RPCException("RPC Proxy is closed") 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 { try {
val serialisedArguments = (arguments?.toList() ?: emptyList()).serialize(context = serializationContextWithObservableContext) 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>() val replyFuture = SettableFuture.create<Any>()
sessionAndProducerPool.run { sessionAndProducerPool.run {
val message = it.session.createMessage(false) val message = it.session.createMessage(false)
@ -219,11 +228,11 @@ class RPCClientProxyHandler(
log.debug { log.debug {
val argumentsString = arguments?.joinToString() ?: "" 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) { require(rpcReplyMap.put(replyId, replyFuture) == null) {
"Generated several RPC requests with same ID $rpcId" "Generated several RPC requests with same ID $replyId"
} }
it.producer.send(message) it.producer.send(message)
it.session.commit() it.session.commit()
@ -236,7 +245,7 @@ class RPCClientProxyHandler(
// This must be a checked exception, so wrap it // This must be a checked exception, so wrap it
throw RPCException(e.message ?: "", e) throw RPCException(e.message ?: "", e)
} finally { } finally {
callSiteMap?.remove(rpcId.toLong) callSiteMap?.remove(replyId)
} }
} }
@ -254,7 +263,7 @@ class RPCClientProxyHandler(
when (result) { when (result) {
is Try.Success -> replyFuture.set(result.value) is Try.Success -> replyFuture.set(result.value)
is Try.Failure -> { is Try.Failure -> {
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong) val rpcCallSite = callSiteMap?.get(serverToClient.id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(result.exception, rpcCallSite) if (rpcCallSite != null) addRpcCallSiteToThrowable(result.exception, rpcCallSite)
replyFuture.setException(result.exception) replyFuture.setException(result.exception)
} }
@ -277,7 +286,7 @@ class RPCClientProxyHandler(
} }
// Add call site information on error // Add call site information on error
if (content.isOnError) { if (content.isOnError) {
val rpcCallSite = callSiteMap?.get(serverToClient.id.toLong) val rpcCallSite = callSiteMap?.get(serverToClient.id)
if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite) if (rpcCallSite != null) addRpcCallSiteToThrowable(content.throwable, rpcCallSite)
} }
observable.onNext(content) observable.onNext(content)
@ -385,9 +394,9 @@ class RPCClientProxyHandler(
} }
} }
private typealias RpcObservableMap = Cache<RPCApi.ObservableId, UnicastSubject<Notification<*>>> private typealias RpcObservableMap = Cache<InvocationId, UnicastSubject<Notification<*>>>
private typealias RpcReplyMap = ConcurrentHashMap<RPCApi.RpcRequestId, SettableFuture<Any?>> private typealias RpcReplyMap = ConcurrentHashMap<InvocationId, SettableFuture<Any?>>
private typealias CallSiteMap = ConcurrentHashMap<Long, Throwable?> private typealias CallSiteMap = ConcurrentHashMap<InvocationId, Throwable?>
/** /**
* Holds a context available during Kryo deserialisation of messages that are expected to contain Observables. * 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> { override fun read(kryo: Kryo, input: Input, type: Class<Observable<*>>): Observable<Any> {
val observableContext = kryo.context[RpcObservableContextKey] as ObservableContext 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<*>>() val observable = UnicastSubject.create<Notification<*>>()
require(observableContext.observableMap.getIfPresent(observableId) == null) { require(observableContext.observableMap.getIfPresent(observableId) == null) {
"Multiple Observables arrived with the same ID $observableId" "Multiple Observables arrived with the same ID $observableId"
} }
val rpcCallSite = getRpcCallSite(kryo, observableContext) val rpcCallSite = getRpcCallSite(kryo, observableContext)
observableContext.observableMap.put(observableId, observable) 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 // 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. // don't need to store a reference to the Observables themselves.
return pinInSubscriptions(observable, observableContext.hardReferenceStore).doOnUnsubscribe { return pinInSubscriptions(observable, observableContext.hardReferenceStore).doOnUnsubscribe {
@ -444,12 +453,19 @@ object RpcClientObservableSerializer : Serializer<Observable<*>>() {
}.dematerialize() }.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<*>) { override fun write(kryo: Kryo, output: Output, observable: Observable<*>) {
throw UnsupportedOperationException("Cannot serialise Observables on the client side") throw UnsupportedOperationException("Cannot serialise Observables on the client side")
} }
private fun getRpcCallSite(kryo: Kryo, observableContext: ObservableContext): Throwable? { 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) return observableContext.callSiteMap?.get(rpcRequestOrObservableId)
} }
} }

View File

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

View File

@ -6,10 +6,10 @@ import net.corda.core.internal.concurrent.map
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.rpcTestUser import net.corda.testing.internal.rpcTestUser
import net.corda.testing.startInVmRpcClient import net.corda.testing.internal.startInVmRpcClient
import net.corda.testing.startRpcClient import net.corda.testing.internal.startRpcClient
import org.apache.activemq.artemis.api.core.client.ClientSession import org.apache.activemq.artemis.api.core.client.ClientSession
import org.junit.runners.Parameterized import org.junit.runners.Parameterized

View File

@ -7,9 +7,9 @@ import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.messaging.rpcContext import net.corda.node.services.messaging.rpcContext
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver import net.corda.testing.internal.rpcDriver
import net.corda.testing.rpcTestUser import net.corda.testing.internal.rpcTestUser
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -57,6 +57,7 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
inner class TestOpsImpl : TestOps { inner class TestOpsImpl : TestOps {
override val protocolVersion = 1 override val protocolVersion = 1
// do not remove Unit
override fun barf(): Unit = throw IllegalArgumentException("Barf!") override fun barf(): Unit = throw IllegalArgumentException("Barf!")
override fun void() {} override fun void() {}
override fun someCalculation(str: String, num: Int) = "$str $num" override fun someCalculation(str: String, num: Int) = "$str $num"
@ -64,8 +65,9 @@ class ClientRPCInfrastructureTests : AbstractRPCTest() {
override fun makeListenableFuture() = doneFuture(1) override fun makeListenableFuture() = doneFuture(1)
override fun makeComplicatedObservable() = complicatedObservable override fun makeComplicatedObservable() = complicatedObservable
override fun makeComplicatedListenableFuture() = complicatedListenableFuturee override fun makeComplicatedListenableFuture() = complicatedListenableFuturee
// do not remove Unit
override fun addedLater(): Unit = throw IllegalStateException() override fun addedLater(): Unit = throw IllegalStateException()
override fun captureUser(): String = rpcContext().currentUser.username override fun captureUser(): String = rpcContext().invocation.principal().name
} }
@Test @Test

View File

@ -7,8 +7,8 @@ import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver import net.corda.testing.internal.rpcDriver
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized

View File

@ -5,8 +5,8 @@ import net.corda.core.concurrent.CordaFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.rpcDriver import net.corda.testing.internal.rpcDriver
import net.corda.testing.startRpcClient import net.corda.testing.internal.startRpcClient
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test

View File

@ -5,14 +5,14 @@ import net.corda.client.rpc.internal.RPCClientConfiguration
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds 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.node.services.messaging.RPCServerConfiguration
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.measure import net.corda.testing.measure
import net.corda.testing.performance.startPublishingFixedRateInjector import net.corda.testing.internal.performance.startPublishingFixedRateInjector
import net.corda.testing.performance.startReporter import net.corda.testing.internal.performance.startReporter
import net.corda.testing.performance.startTightLoopInjector import net.corda.testing.internal.performance.startTightLoopInjector
import net.corda.testing.rpcDriver import net.corda.testing.internal.rpcDriver
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith

View File

@ -4,10 +4,9 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.messaging.rpcContext import net.corda.node.services.messaging.rpcContext
import net.corda.node.services.messaging.requirePermission
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.rpcDriver import net.corda.testing.internal.rpcDriver
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized

View File

@ -14,11 +14,8 @@ import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.testing.ALICE_NAME import net.corda.testing.*
import net.corda.testing.BOB_NAME
import net.corda.testing.CHARLIE_NAME
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test

View File

@ -3,10 +3,12 @@ package net.corda.confidential
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.Before import org.junit.Before
import net.corda.testing.node.MockNodeParameters
import org.junit.Test import org.junit.Test
import kotlin.test.* import kotlin.test.*
@ -83,28 +85,30 @@ class SwapIdentitiesFlowTests {
val notaryNode = mockNet.defaultNotaryNode val notaryNode = mockNet.defaultNotaryNode
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.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 // Check that the wrong signature is rejected
notaryNode.database.transaction { notaryNode.database.transaction {
notaryNode.services.keyManagementService.freshKeyAndCert(notaryNode.services.myInfo.chooseIdentityAndCert(), false) notaryNode.services.keyManagementService.freshKeyAndCert(notary, false)
}.let { anonymousNotary -> }.let { anonymousNotary ->
val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousNotary) val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousNotary)
val signature = notaryNode.services.keyManagementService.sign(sigData, anonymousNotary.owningKey) val signature = notaryNode.services.keyManagementService.sign(sigData, anonymousNotary.owningKey)
assertFailsWith<SwapIdentitiesException>("Signature does not match the given identity and nonce") { 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 // Check that the right signing key, but wrong identity is rejected
val anonymousAlice = aliceNode.database.transaction { val anonymousAlice: PartyAndCertificate = aliceNode.database.transaction {
aliceNode.services.keyManagementService.freshKeyAndCert(aliceNode.services.myInfo.chooseIdentityAndCert(), false) aliceNode.services.keyManagementService.freshKeyAndCert(alice, false)
} }
bobNode.database.transaction { bobNode.database.transaction {
bobNode.services.keyManagementService.freshKeyAndCert(bobNode.services.myInfo.chooseIdentityAndCert(), false) bobNode.services.keyManagementService.freshKeyAndCert(bob, false)
}.let { anonymousBob -> }.let { anonymousBob ->
val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousAlice) val sigData = SwapIdentitiesFlow.buildDataToSign(anonymousAlice)
val signature = bobNode.services.keyManagementService.sign(sigData, anonymousBob.owningKey) val signature = bobNode.services.keyManagementService.sign(sigData, anonymousBob.owningKey)
assertFailsWith<SwapIdentitiesException>("Signature does not match the given identity and nonce.") { 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())
} }
} }

View File

@ -13,7 +13,7 @@
<Appenders> <Appenders>
<Console name="Console-Appender" target="SYSTEM_OUT"> <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> </Console>
<!-- Required for printBasicInfo --> <!-- Required for printBasicInfo -->
@ -27,7 +27,7 @@
fileName="${sys:log-path}/${log-name}.log" fileName="${sys:log-path}/${log-name}.log"
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz"> 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> <Policies>
<TimeBasedTriggeringPolicy/> <TimeBasedTriggeringPolicy/>

View File

@ -117,6 +117,9 @@ dependencies {
// JPA 2.1 annotations. // JPA 2.1 annotations.
compile "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final" 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) // TODO Consider moving it to quasar-utils in the future (introduced with PR-1388)

View File

@ -0,0 +1,9 @@
package net.corda.core.context
import net.corda.core.serialization.CordaSerializable
/**
* Authentication / Authorisation Service ID.
*/
@CordaSerializable
data class AuthServiceId(val value: String)

View File

@ -0,0 +1,136 @@
package net.corda.core.context
import net.corda.core.contracts.ScheduledStateRef
import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.CordaSerializable
import java.security.Principal
/**
* Models the information needed to trace an invocation in Corda.
* Includes initiating actor, origin, trace information, and optional external trace information to correlate clients' IDs.
*
* @param origin origin of the invocation.
* @param trace Corda invocation trace.
* @param actor acting agent of the invocation, used to derive the security principal.
* @param externalTrace optional external invocation trace for cross-system logs correlation.
* @param impersonatedActor optional impersonated actor, used for logging but not for authorisation.
*/
@CordaSerializable
data class InvocationContext(val origin: Origin, val trace: Trace, val actor: Actor?, val externalTrace: Trace? = null, val impersonatedActor: Actor? = null) {
companion object {
/**
* Creates an [InvocationContext] with a [Trace] that defaults to a [java.util.UUID] as value and [java.time.Instant.now] timestamp.
*/
@JvmStatic
fun newInstance(origin: Origin, trace: Trace = Trace.newInstance(), actor: Actor? = null, externalTrace: Trace? = null, impersonatedActor: Actor? = null) = InvocationContext(origin, trace, actor, externalTrace, impersonatedActor)
/**
* Creates an [InvocationContext] with [Origin.RPC] origin.
*/
@JvmStatic
fun rpc(actor: Actor, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.RPC(actor), trace, actor, externalTrace, impersonatedActor)
/**
* Creates an [InvocationContext] with [Origin.Peer] origin.
*/
@JvmStatic
fun peer(party: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null, impersonatedActor: Actor? = null): InvocationContext = newInstance(Origin.Peer(party), trace, null, externalTrace, impersonatedActor)
/**
* Creates an [InvocationContext] with [Origin.Service] origin.
*/
@JvmStatic
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Service(serviceClassName, owningLegalIdentity), trace, null, externalTrace)
/**
* Creates an [InvocationContext] with [Origin.Scheduled] origin.
*/
@JvmStatic
fun scheduled(scheduledState: ScheduledStateRef, trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = newInstance(Origin.Scheduled(scheduledState), trace, null, externalTrace)
/**
* Creates an [InvocationContext] with [Origin.Shell] origin.
*/
@JvmStatic
fun shell(trace: Trace = Trace.newInstance(), externalTrace: Trace? = null): InvocationContext = InvocationContext(Origin.Shell, trace, null, externalTrace)
}
/**
* Associated security principal.
*/
fun principal(): Principal = origin.principal()
}
/**
* Models an initiator in Corda, can be a user, a service, etc.
*/
@CordaSerializable
data class Actor(val id: Id, val serviceId: AuthServiceId, val owningLegalIdentity: CordaX500Name) {
companion object {
@JvmStatic
fun service(serviceClassName: String, owningLegalIdentity: CordaX500Name): Actor = Actor(Id(serviceClassName), AuthServiceId("SERVICE"), owningLegalIdentity)
}
/**
* Actor id.
*/
@CordaSerializable
data class Id(val value: String)
}
/**
* Invocation origin for tracing purposes.
*/
@CordaSerializable
sealed class Origin {
/**
* Returns the [Principal] for a given [Actor].
*/
abstract fun principal(): Principal
/**
* Origin was an RPC call.
*/
data class RPC(private val actor: Actor) : Origin() {
override fun principal() = Principal { actor.id.value }
}
/**
* Origin was a message sent by a [Peer].
*/
data class Peer(val party: CordaX500Name) : Origin() {
override fun principal() = Principal { party.toString() }
}
/**
* Origin was a Corda Service.
*/
data class Service(val serviceClassName: String, val owningLegalIdentity: CordaX500Name) : Origin() {
override fun principal() = Principal { serviceClassName }
}
/**
* Origin was a scheduled activity.
*/
data class Scheduled(val scheduledState: ScheduledStateRef) : Origin() {
override fun principal() = Principal { "Scheduler" }
}
// TODO When proper ssh access enabled, add username/use RPC?
/**
* Origin was the Shell.
*/
object Shell : Origin() {
override fun principal() = Principal { "Shell User" }
}
}

View File

@ -0,0 +1,56 @@
package net.corda.core.context
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.Id
import net.corda.core.utilities.UuidGenerator
import java.time.Instant
/**
* Contextual tracing information, including invocation and session id.
*/
@CordaSerializable
data class Trace(val invocationId: InvocationId, val sessionId: SessionId) {
companion object {
/**
* Creates a trace using a [InvocationId.newInstance] with default arguments and a [SessionId] matching the value and timestamp from the invocation id..
*/
@JvmStatic
fun newInstance(invocationId: InvocationId = InvocationId.newInstance(), sessionId: SessionId = SessionId(invocationId.value, invocationId.timestamp)) = Trace(invocationId, sessionId)
}
/**
* Represents id and timestamp of an invocation.
*/
@CordaSerializable
class InvocationId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
companion object {
private val TYPE = "Invocation"
/**
* Creates an invocation id using a [java.util.UUID] as value and [Instant.now] as timestamp.
*/
@JvmStatic
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = InvocationId(value, timestamp)
}
}
/**
* Represents id and timestamp of a session.
*/
@CordaSerializable
class SessionId(value: String, timestamp: Instant) : Id<String>(value, TYPE, timestamp) {
companion object {
private val TYPE = "Session"
/**
* Creates a session id using a [java.util.UUID] as value and [Instant.now] as timestamp.
*/
@JvmStatic
fun newInstance(value: String = UuidGenerator.next().toString(), timestamp: Instant = Instant.now()) = SessionId(value, timestamp)
}
}
}

View File

@ -45,9 +45,17 @@ interface NamedByHash {
*/ */
@CordaSerializable @CordaSerializable
data class Issued<out P : Any>(val issuer: PartyAndReference, val product: P) { 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" 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 * 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 * 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 * 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. * 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 data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instant) : Scheduled
/** /**

View File

@ -103,3 +103,5 @@ fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this)
* Compute the SHA-256 hash for the contents of the [OpaqueBytes]. * Compute the SHA-256 hash for the contents of the [OpaqueBytes].
*/ */
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes) fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes)

View File

@ -91,7 +91,7 @@ class CollectSignaturesFlow @JvmOverloads constructor(val partiallySignedTx: Sig
} }
// The signatures must be valid and the transaction must be valid. // The signatures must be valid and the transaction must be valid.
partiallySignedTx.verifySignaturesExcept(*notSigned.toTypedArray()) partiallySignedTx.verifySignaturesExcept(notSigned)
partiallySignedTx.tx.toLedgerTransaction(serviceHub).verify() partiallySignedTx.tx.toLedgerTransaction(serviceHub).verify()
// Determine who still needs to sign. // Determine who still needs to sign.
@ -251,7 +251,7 @@ abstract class SignTransactionFlow(val otherSideSession: FlowSession,
val signed = stx.sigs.map { it.by } val signed = stx.sigs.map { it.by }
val allSigners = stx.tx.requiredSigningKeys val allSigners = stx.tx.requiredSigningKeys
val notSigned = allSigners - signed val notSigned = allSigners - signed
stx.verifySignaturesExcept(*notSigned.toTypedArray()) stx.verifySignaturesExcept(notSigned)
} }
/** /**

View File

@ -10,29 +10,35 @@ import java.security.Principal
* communication started by peer node [FlowInitiator.Peer], scheduled flows [FlowInitiator.Scheduled] * communication started by peer node [FlowInitiator.Peer], scheduled flows [FlowInitiator.Scheduled]
* or via the Corda Shell [FlowInitiator.Shell]. * or via the Corda Shell [FlowInitiator.Shell].
*/ */
@Deprecated("Do not use these types. Future releases might remove them.")
@CordaSerializable @CordaSerializable
sealed class FlowInitiator : Principal { sealed class FlowInitiator : Principal {
/** Started using [net.corda.core.messaging.CordaRPCOps.startFlowDynamic]. */ /** 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() { data class RPC(val username: String) : FlowInitiator() {
override fun getName(): String = username override fun getName(): String = username
} }
/** Started when we get new session initiation request. */ /** 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() { data class Peer(val party: Party) : FlowInitiator() {
override fun getName(): String = party.name.toString() override fun getName(): String = party.name.toString()
} }
/** Started by a CordaService. */ /** Started by a CordaService. */
@Deprecated("Do not use this type. Future releases might remove it.")
data class Service(val serviceClassName: String) : FlowInitiator() { data class Service(val serviceClassName: String) : FlowInitiator() {
override fun getName(): String = serviceClassName override fun getName(): String = serviceClassName
} }
/** Started as scheduled activity. */ /** Started as scheduled activity. */
@Deprecated("Do not use this type. Future releases might remove it.")
data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() { data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() {
override fun getName(): String = "Scheduler" override fun getName(): String = "Scheduler"
} }
// TODO When proper ssh access enabled, add username/use RPC? // TODO When proper ssh access enabled, add username/use RPC?
@Deprecated("Do not use this type. Future releases might remove it.")
object Shell : FlowInitiator() { object Shell : FlowInitiator() {
override fun getName(): String = "Shell User" override fun getName(): String = "Shell User"
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.context.InvocationContext
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@ -55,7 +56,7 @@ interface FlowStateMachine<R> {
val logger: Logger val logger: Logger
val id: StateMachineRunId val id: StateMachineRunId
val resultFuture: CordaFuture<R> val resultFuture: CordaFuture<R>
val flowInitiator: FlowInitiator val context: InvocationContext
val ourIdentityAndCert: PartyAndCertificate val ourIdentityAndCert: PartyAndCertificate
@Suspendable @Suspendable

View File

@ -19,6 +19,8 @@ import rx.subjects.UnicastSubject
import java.io.* import java.io.*
import java.lang.reflect.Field import java.lang.reflect.Field
import java.math.BigDecimal import java.math.BigDecimal
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8 import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.* 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. */ /** Convenience method to get the package name of a class literal. */
val KClass<*>.packageName get() = java.`package`.name val KClass<*>.packageName get() = java.`package`.name
fun URL.openHttpConnection(): HttpURLConnection = openConnection() as HttpURLConnection

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

View File

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

View File

@ -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. * 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. * The values are in the same order as the futures in the collection, not the order of completion.

View File

@ -27,8 +27,12 @@ object NodeInfoSchemaV1 : MappedSchema(
@Column(name = "node_info_id") @Column(name = "node_info_id")
var id: Int, var id: Int,
@Column(name="node_info_hash", length = 64)
val hash: String,
@Column(name = "addresses") @Column(name = "addresses")
@OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true) @OneToMany(cascade = arrayOf(CascadeType.ALL), orphanRemoval = true)
@JoinColumn(name = "node_info_id")
val addresses: List<NodeInfoSchemaV1.DBHostAndPort>, val addresses: List<NodeInfoSchemaV1.DBHostAndPort>,
@Column(name = "legal_identities_certs") @Column(name = "legal_identities_certs")
@ -66,6 +70,7 @@ object NodeInfoSchemaV1 : MappedSchema(
) : Serializable ) : Serializable
@Entity @Entity
@Table(name = "node_info_hosts")
data class DBHostAndPort( data class DBHostAndPort(
@EmbeddedId @EmbeddedId
private val pk: PKHostAndPort private val pk: PKHostAndPort
@ -91,7 +96,6 @@ object NodeInfoSchemaV1 : MappedSchema(
@Column(name = "party_name", nullable = false) @Column(name = "party_name", nullable = false)
val name: String, val name: String,
@Lob
@Column(name = "owning_key_hash", length = MAX_HASH_HEX_SIZE) @Column(name = "owning_key_hash", length = MAX_HASH_HEX_SIZE)
val owningKeyHash: String, val owningKeyHash: String,

View File

@ -1,6 +1,10 @@
package net.corda.core.messaging package net.corda.core.messaging
import net.corda.core.concurrent.CordaFuture 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.contracts.ContractState
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowInitiator 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.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo 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.NetworkMapCache
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultQueryException import net.corda.core.node.services.VaultQueryException
import net.corda.core.node.services.vault.DEFAULT_PAGE_SIZE import net.corda.core.node.services.vault.*
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.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.Try import net.corda.core.utilities.Try
@ -25,13 +27,41 @@ import java.io.InputStream
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
private val unknownName = CordaX500Name("UNKNOWN", "UNKNOWN", "GB")
@CordaSerializable @CordaSerializable
data class StateMachineInfo( data class StateMachineInfo @JvmOverloads constructor(
val id: StateMachineRunId, val id: StateMachineRunId,
val flowLogicClassName: String, val flowLogicClassName: String,
val initiator: FlowInitiator, 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)" 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. */ /** Returns Node's NodeInfo, assuming this will not change while the node is running. */
fun nodeInfo(): NodeInfo 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. * 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. */ /** Uploads a jar to the node, returns it's hash. */
fun uploadAttachment(jar: InputStream): SecureHash 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. */ /** Returns the node's current time. */
fun currentNodeTime(): Instant fun currentNodeTime(): Instant
@ -428,3 +468,8 @@ inline fun <T, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startTrac
*/ */
@CordaSerializable @CordaSerializable
data class DataFeed<out A, B>(val snapshot: A, val updates: Observable<B>) data class DataFeed<out A, B>(val snapshot: A, val updates: Observable<B>)
@CordaSerializable
enum class NodeState {
SHUTTING_DOWN
}

View File

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

View File

@ -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. */ /** Returns true if [party] is one of the identities of this node, else false. */
fun isLegalIdentity(party: Party): Boolean = party in legalIdentities 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\"") * Get a legal identity of this node from the X.500 name. This is intended for use in cases where the node is
return identity.party * 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\"")
} }
} }

View File

@ -8,11 +8,13 @@ import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.messaging.NodeState
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import rx.Observable
import java.security.PublicKey import java.security.PublicKey
import java.sql.Connection import java.sql.Connection
import java.time.Clock import java.time.Clock
@ -29,6 +31,16 @@ interface StateLoader {
*/ */
@Throws(TransactionResolutionException::class) @Throws(TransactionResolutionException::class)
fun loadState(stateRef: StateRef): TransactionState<*> 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. */ /** The [NodeInfo] object corresponding to our own entry in the network map. */
val myInfo: NodeInfo 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 * 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. * [CordaService] and will have automatically been registered by the node.

View File

@ -3,6 +3,8 @@ package net.corda.core.node.services
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash 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.IOException
import java.io.InputStream import java.io.InputStream
import java.nio.file.FileAlreadyExistsException import java.nio.file.FileAlreadyExistsException
@ -33,5 +35,23 @@ interface AttachmentStorage {
*/ */
@Throws(FileAlreadyExistsException::class, IOException::class) @Throws(FileAlreadyExistsException::class, IOException::class)
fun importAttachment(jar: InputStream): AttachmentId 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>
} }

View File

@ -2,6 +2,7 @@ package net.corda.core.node.services
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party 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. * 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> val notaryIdentities: List<Party>
// DOCEND 1 // DOCEND 1
@ -115,7 +117,7 @@ interface NetworkMapCacheBase {
fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name } fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }
// DOCEND 2 // 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 fun isNotary(party: Party): Boolean = party in notaryIdentities
/** /**

View File

@ -15,13 +15,38 @@ import java.time.Instant
import java.util.* import java.util.*
import javax.persistence.criteria.Predicate 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: * Indexing assumptions:
* QueryCriteria assumes underlying schema tables are correctly indexed for performance. * QueryCriteria assumes underlying schema tables are correctly indexed for performance.
*/ */
@CordaSerializable @CordaSerializable
sealed class QueryCriteria { sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaParser>, GenericQueryCriteria.ChainableQueryCriteria<QueryCriteria, IQueryCriteriaParser> {
abstract fun visit(parser: IQueryCriteriaParser): Collection<Predicate>
@CordaSerializable @CordaSerializable
data class TimeCondition(val type: TimeInstantType, val predicate: ColumnPredicate<Instant>) 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] // timestamps stored in the vault states table [VaultSchema.VaultStates]
@CordaSerializable @CordaSerializable
enum class TimeInstantType { enum class TimeInstantType {
@ -141,18 +153,47 @@ sealed class QueryCriteria {
CONSUMED CONSUMED
} }
infix fun and(criteria: QueryCriteria): QueryCriteria = AndComposition(this, criteria) class AndComposition(override val a: QueryCriteria, override val b: QueryCriteria): QueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.AndVisitor<QueryCriteria, IQueryCriteriaParser, Sort>
infix fun or(criteria: QueryCriteria): QueryCriteria = OrComposition(this, criteria) 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 @DoNotImplement
interface IQueryCriteriaParser { interface IQueryCriteriaParser : BaseQueryCriteriaParser<QueryCriteria, IQueryCriteriaParser, Sort> {
fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate> fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate>
fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate> fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate>
fun parseCriteria(criteria: QueryCriteria.LinearStateQueryCriteria): Collection<Predicate> fun parseCriteria(criteria: QueryCriteria.LinearStateQueryCriteria): Collection<Predicate>
fun <L : PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate> fun <L : PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate>
fun parseCriteria(criteria: QueryCriteria.VaultQueryCriteria): 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>
} }

View File

@ -127,12 +127,14 @@ data class PageSpecification(val pageNumber: Int = -1, val pageSize: Int = DEFAU
val isDefault = (pageSize == DEFAULT_PAGE_SIZE && pageNumber == -1) 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 * 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. * and null handling, to be applied upon processing a query specification.
*/ */
@CordaSerializable @CordaSerializable
data class Sort(val columns: Collection<SortColumn>) { data class Sort(val columns: Collection<SortColumn>) : BaseSort() {
@CordaSerializable @CordaSerializable
enum class Direction { enum class Direction {
ASC, ASC,
@ -177,6 +179,21 @@ data class Sort(val columns: Collection<SortColumn>) {
val direction: Sort.Direction = Sort.Direction.ASC) 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 @CordaSerializable
sealed class SortAttribute { 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>> 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>> `in`(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection)
fun <R : Comparable<R>> notIn(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.NOT_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)) fun <O> KProperty1<O, String?>.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
@JvmStatic @JvmStatic

View File

@ -1,12 +1,12 @@
package net.corda.core.schemas package net.corda.core.schemas
import net.corda.core.contracts.ContractState import net.corda.core.contracts.*
import net.corda.core.contracts.FungibleAsset
import net.corda.core.contracts.OwnableState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import org.hibernate.annotations.Type
import java.util.* 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 * JPA representation of the common schema entities
@ -74,7 +74,8 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers
@Column(name = "issuer_name") @Column(name = "issuer_name")
var issuer: AbstractParty, var issuer: AbstractParty,
@Column(name = "issuer_reference") @Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
@Type(type = "corda-wrapper-binary")
var issuerRef: ByteArray var issuerRef: ByteArray
) : PersistentState() ) : PersistentState()
} }

View File

@ -2,8 +2,7 @@ package net.corda.core.serialization
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.WriteOnceProperty import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.sequence 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 * 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]). * 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?>() 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. * 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, * 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). * Global singletons to be used as defaults that are injected elsewhere (generally, in the node or in RPC client).
*/ */
object SerializationDefaults : SerializationEnvironment { object SerializationDefaults {
override var SERIALIZATION_FACTORY: SerializationFactory by WriteOnceProperty() val SERIALIZATION_FACTORY get() = effectiveSerializationEnv.serializationFactory
override var P2P_CONTEXT: SerializationContext by WriteOnceProperty() val P2P_CONTEXT get() = effectiveSerializationEnv.p2pContext
override var RPC_SERVER_CONTEXT: SerializationContext by WriteOnceProperty() val RPC_SERVER_CONTEXT get() = effectiveSerializationEnv.rpcServerContext
override var RPC_CLIENT_CONTEXT: SerializationContext by WriteOnceProperty() val RPC_CLIENT_CONTEXT get() = effectiveSerializationEnv.rpcClientContext
override var STORAGE_CONTEXT: SerializationContext by WriteOnceProperty() val STORAGE_CONTEXT get() = effectiveSerializationEnv.storageContext
override var CHECKPOINT_CONTEXT: SerializationContext by WriteOnceProperty() val CHECKPOINT_CONTEXT get() = effectiveSerializationEnv.checkpointContext
} }
/** /**

View File

@ -1,13 +1,61 @@
package net.corda.core.serialization.internal 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.SerializationContext
import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.SerializationFactory
interface SerializationEnvironment { interface SerializationEnvironment {
val SERIALIZATION_FACTORY: SerializationFactory val serializationFactory: SerializationFactory
val P2P_CONTEXT: SerializationContext val p2pContext: SerializationContext
val RPC_SERVER_CONTEXT: SerializationContext val rpcServerContext: SerializationContext
val RPC_CLIENT_CONTEXT: SerializationContext val rpcClientContext: SerializationContext
val STORAGE_CONTEXT: SerializationContext val storageContext: SerializationContext
val CHECKPOINT_CONTEXT: 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

View File

@ -9,10 +9,16 @@ import net.corda.core.utilities.toNonEmptySet
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.*
/** An interface for transactions containing signatures, with logic for signature verification */ /** An interface for transactions containing signatures, with logic for signature verification */
@DoNotImplement @DoNotImplement
interface TransactionWithSignatures : NamedByHash { interface TransactionWithSignatures : NamedByHash {
/**
* List of signatures on this transaction.
* @see checkSignaturesAreValid
* @see verifyRequiredSignatures
*/
val sigs: List<TransactionSignature> val sigs: List<TransactionSignature>
/** Specifies all the public keys that require signatures for the transaction to be valid */ /** 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 SignaturesMissingException if any signatures should have been present but were not.
*/ */
@Throws(SignatureException::class) @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. * 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) @Throws(SignatureException::class)
fun verifySignaturesExcept(vararg allowedToBeMissing: PublicKey) { 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() checkSignaturesAreValid()
val needed = getMissingSigners() - allowedToBeMissing 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 * 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 * 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 * 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 InvalidKeyException if the key on a signature is invalid.
* @throws SignatureException if a signature fails to verify. * @throws SignatureException if a signature fails to verify.
@ -80,7 +103,6 @@ interface TransactionWithSignatures : NamedByHash {
val sigKeys = sigs.map { it.by }.toSet() val sigKeys = sigs.map { it.by }.toSet()
// TODO Problem is that we can get single PublicKey wrapped as CompositeKey in allowedToBeMissing/mustSign // 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?) // 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 requiredSigningKeys.filter { !it.isFulfilledBy(sigKeys) }.toSet()
return missing
} }
} }

View File

@ -0,0 +1,46 @@
package net.corda.core.utilities
import java.time.Instant
import java.time.Instant.now
/**
* Represents a unique, timestamped id.
* @param value unique value of the id.
* @param entityType optional id entity type.
* @param timestamp timestamp for the id.
*/
open class Id<out VALUE : Any>(val value: VALUE, val entityType: String?, val timestamp: Instant) {
companion object {
/**
* Creates an id using [Instant.now] as timestamp.
*/
@JvmStatic
fun <V : Any> newInstance(value: V, entityType: String? = null, timestamp: Instant = now()) = Id(value, entityType, timestamp)
}
final override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Id<*>
if (value != other.value) return false
if (entityType != other.entityType) return false
return true
}
final override fun hashCode(): Int {
var result = value.hashCode()
result = 31 * result + (entityType?.hashCode() ?: 0)
return result
}
final override fun toString(): String {
return "$value, timestamp: $timestamp" + (entityType?.let { ", entityType: $it" } ?: "")
}
}

View File

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

View File

@ -4,8 +4,8 @@ import co.paralleluniverse.fibers.Suspendable;
import com.google.common.primitives.Primitives; import com.google.common.primitives.Primitives;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.node.internal.StartedNode; import net.corda.node.internal.StartedNode;
import net.corda.testing.node.MockNetwork;
import net.corda.testing.TestConstants; import net.corda.testing.TestConstants;
import net.corda.testing.node.MockNetwork;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -13,22 +13,22 @@ import org.junit.Test;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; 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.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static net.corda.testing.NodeTestUtils.startFlow;
public class FlowsInJavaTest { public class FlowsInJavaTest {
private final MockNetwork mockNet = new MockNetwork(); private final MockNetwork mockNet = new MockNetwork();
private StartedNode<MockNetwork.MockNode> aliceNode; private StartedNode<MockNetwork.MockNode> aliceNode;
private StartedNode<MockNetwork.MockNode> bobNode; private StartedNode<MockNetwork.MockNode> bobNode;
private Party bob;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName()); aliceNode = mockNet.createPartyNode(TestConstants.getALICE().getName());
bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName()); bobNode = mockNet.createPartyNode(TestConstants.getBOB().getName());
mockNet.runNetwork(); bob = singleIdentity(bobNode.getInfo());
// Ensure registration was successful
aliceNode.getInternals().getNodeReadyFuture().get();
} }
@After @After
@ -39,7 +39,7 @@ public class FlowsInJavaTest {
@Test @Test
public void suspendableActionInsideUnwrap() throws Exception { public void suspendableActionInsideUnwrap() throws Exception {
bobNode.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class); 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(); mockNet.runNetwork();
assertThat(result.get()).isEqualTo("Hello"); assertThat(result.get()).isEqualTo("Hello");
} }
@ -54,8 +54,8 @@ public class FlowsInJavaTest {
} }
private void primitiveReceiveTypeTest(Class<?> receiveType) throws InterruptedException { private void primitiveReceiveTypeTest(Class<?> receiveType) throws InterruptedException {
PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(chooseIdentity(bobNode.getInfo()), receiveType); PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(bob, receiveType);
Future<?> result = aliceNode.getServices().startFlow(flow).getResultFuture(); Future<?> result = startFlow(aliceNode.getServices(), flow).getResultFuture();
mockNet.runNetwork(); mockNet.runNetwork();
try { try {
result.get(); result.get();

View File

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

View File

@ -10,6 +10,7 @@ import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.* import net.corda.testing.*
import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.security.PublicKey import java.security.PublicKey
@ -23,44 +24,46 @@ class PartialMerkleTreeTest {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val nodes = "abcdef" private val nodes = "abcdef"
private val hashed = nodes.map { node -> private lateinit var hashed: List<SecureHash.SHA256>
withTestSerialization { private lateinit var expectedRoot: SecureHash
node.serialize().sha256() private lateinit var merkleTree: MerkleTree
} private lateinit var testLedger: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
} private lateinit var txs: List<WireTransaction>
private val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash private lateinit var testTx: WireTransaction
private val merkleTree = MerkleTree.getMerkleTree(hashed) @Before
fun init() {
private val testLedger = ledger { hashed = nodes.map { it.serialize().sha256() }
unverifiedTransaction { expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash
attachments(Cash.PROGRAM_ID) merkleTree = MerkleTree.getMerkleTree(hashed)
output(Cash.PROGRAM_ID, "MEGA_CORP cash") { testLedger = ledger {
Cash.State( unverifiedTransaction {
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), attachments(Cash.PROGRAM_ID)
owner = MEGA_CORP 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") { transaction {
Cash.State( attachments(Cash.PROGRAM_ID)
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), input("MEGA_CORP cash")
owner = MINI_CORP 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
transaction { testTx = txs[0]
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()
}
} }
private val txs = testLedger.interpreter.transactionsToVerify
private val testTx = txs[0]
// Building full Merkle Tree tests. // Building full Merkle Tree tests.
@Test @Test
fun `building Merkle tree with 6 nodes - no rightmost nodes`() { fun `building Merkle tree with 6 nodes - no rightmost nodes`() {

View File

@ -10,12 +10,9 @@ import net.corda.core.internal.FetchDataFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.testing.ALICE import net.corda.testing.*
import net.corda.testing.ALICE_NAME
import net.corda.testing.BOB
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import net.corda.testing.singleIdentity
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -54,8 +51,6 @@ class AttachmentTests {
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.name) val bobNode = mockNet.createPartyNode(BOB.name)
// Ensure that registration was successful before progressing any further
mockNet.runNetwork()
val alice = aliceNode.info.singleIdentity() val alice = aliceNode.info.singleIdentity()
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
@ -91,15 +86,11 @@ class AttachmentTests {
val aliceNode = mockNet.createPartyNode(ALICE.name) val aliceNode = mockNet.createPartyNode(ALICE.name)
val bobNode = mockNet.createPartyNode(BOB.name) val bobNode = mockNet.createPartyNode(BOB.name)
// Ensure that registration was successful before progressing any further
mockNet.runNetwork()
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) bobNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)
// Get node one to fetch a non-existent attachment. // Get node one to fetch a non-existent attachment.
val hash = SecureHash.randomSHA256() val hash = SecureHash.randomSHA256()
mockNet.runNetwork()
val alice = aliceNode.info.singleIdentity() val alice = aliceNode.info.singleIdentity()
val bobFlow = bobNode.startAttachmentFlow(setOf(hash), alice) val bobFlow = bobNode.startAttachmentFlow(setOf(hash), alice)
mockNet.runNetwork() mockNet.runNetwork()
@ -116,7 +107,6 @@ class AttachmentTests {
} }
}) })
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name))
mockNet.runNetwork()
val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)
aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) aliceNode.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java)

View File

@ -10,7 +10,6 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
@ -23,10 +22,6 @@ import kotlin.reflect.KClass
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class CollectSignaturesFlowTests { class CollectSignaturesFlowTests {
companion object {
private val cordappPackages = listOf("net.corda.testing.contracts")
}
private lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
private lateinit var aliceNode: StartedNode<MockNetwork.MockNode> private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
private lateinit var bobNode: StartedNode<MockNetwork.MockNode> private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
@ -38,11 +33,10 @@ class CollectSignaturesFlowTests {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = cordappPackages) mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts"))
aliceNode = mockNet.createPartyNode(ALICE.name) aliceNode = mockNet.createPartyNode(ALICE.name)
bobNode = mockNet.createPartyNode(BOB.name) bobNode = mockNet.createPartyNode(BOB.name)
charlieNode = mockNet.createPartyNode(CHARLIE.name) charlieNode = mockNet.createPartyNode(CHARLIE.name)
mockNet.runNetwork()
alice = aliceNode.info.singleIdentity() alice = aliceNode.info.singleIdentity()
bob = bobNode.info.singleIdentity() bob = bobNode.info.singleIdentity()
charlie = charlieNode.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 // 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 // override "checkTransaction" and add whatever logic their require to verify the SignedTransaction they are
// receiving off the wire. // receiving off the wire.
object TestFlowTwo { object TestFlow {
@InitiatingFlow @InitiatingFlow
class Initiator(private val state: DummyContract.MultiOwnerState) : FlowLogic<SignedTransaction>() { class Initiator(private val state: DummyContract.MultiOwnerState, private val notary: Party) : FlowLogic<SignedTransaction>() {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary = serviceHub.getDefaultNotary()
val myInputKeys = state.participants.map { it.owningKey } val myInputKeys = state.participants.map { it.owningKey }
val command = Command(DummyContract.Commands.Create(), myInputKeys) val command = Command(DummyContract.Commands.Create(), myInputKeys)
val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DummyContract.PROGRAM_ID), command) 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>() { class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable override fun call() { @Suspendable override fun call() {
val signFlow = object : SignTransactionFlow(otherSideSession) { 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 // Normally this is handled by TransactionKeyFlow, but here we have to manually let A know about the identity
aliceNode.services.identityService.verifyAndRegisterIdentity(bConfidentialIdentity) aliceNode.services.identityService.verifyAndRegisterIdentity(bConfidentialIdentity)
} }
registerFlowOnAllNodes(TestFlowTwo.Responder::class) registerFlowOnAllNodes(TestFlow.Responder::class)
val magicNumber = 1337 val magicNumber = 1337
val parties = listOf(alice, bConfidentialIdentity.party, charlie) val parties = listOf(alice, bConfidentialIdentity.party, charlie)
val state = DummyContract.MultiOwnerState(magicNumber, parties) 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() mockNet.runNetwork()
val result = flow.resultFuture.getOrThrow() val result = flow.resultFuture.getOrThrow()
result.verifyRequiredSignatures() result.verifyRequiredSignatures()
@ -181,7 +129,7 @@ class CollectSignaturesFlowTests {
@Test @Test
fun `fails when not signed by initiator`() { fun `fails when not signed by initiator`() {
val onePartyDummyContract = DummyContract.generateInitial(1337, notary, alice.ref(1)) 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 ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract)
val flow = aliceNode.services.startFlow(CollectSignaturesFlow(ptx, emptySet())) val flow = aliceNode.services.startFlow(CollectSignaturesFlow(ptx, emptySet()))
mockNet.runNetwork() mockNet.runNetwork()

View File

@ -23,6 +23,10 @@ import net.corda.nodeapi.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2 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 net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -33,21 +37,24 @@ import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class ContractUpgradeFlowTest { class ContractUpgradeFlowTest {
lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
lateinit var aliceNode: StartedNode<MockNetwork.MockNode> private lateinit var aliceNode: StartedNode<MockNetwork.MockNode>
lateinit var bobNode: StartedNode<MockNetwork.MockNode> private lateinit var bobNode: StartedNode<MockNetwork.MockNode>
lateinit var notary: Party private lateinit var notary: Party
private lateinit var alice: Party
private lateinit var bob: Party
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows")) mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset", "net.corda.core.flows"))
aliceNode = mockNet.createPartyNode(ALICE.name) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bobNode = mockNet.createPartyNode(BOB.name) bobNode = mockNet.createPartyNode(BOB_NAME)
notary = mockNet.defaultNotaryIdentity
alice = aliceNode.info.singleIdentity()
bob = bobNode.info.singleIdentity()
// Process registration // Process registration
mockNet.runNetwork() mockNet.runNetwork()
notary = mockNet.defaultNotaryIdentity
} }
@After @After
@ -58,11 +65,11 @@ class ContractUpgradeFlowTest {
@Test @Test
fun `2 parties contract upgrade`() { fun `2 parties contract upgrade`() {
// Create dummy contract. // 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 signedByA = aliceNode.services.signInitialTransaction(twoPartyDummyContract)
val stx = bobNode.services.addSignature(signedByA) val stx = bobNode.services.addSignature(signedByA)
aliceNode.services.startFlow(FinalityFlow(stx, setOf(bobNode.info.chooseIdentity()))) aliceNode.services.startFlow(FinalityFlow(stx, setOf(bob)))
mockNet.runNetwork() mockNet.runNetwork()
val atx = aliceNode.database.transaction { aliceNode.services.validatedTransactions.getTransaction(stx.id) } val atx = aliceNode.database.transaction { aliceNode.services.validatedTransactions.getTransaction(stx.id) }
@ -128,7 +135,7 @@ class ContractUpgradeFlowTest {
fun `2 parties contract upgrade using RPC`() { fun `2 parties contract upgrade using RPC`() {
rpcDriver(initialiseSerialization = false) { rpcDriver(initialiseSerialization = false) {
// Create dummy contract. // 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 signedByA = aliceNode.services.signInitialTransaction(twoPartyDummyContract)
val stx = bobNode.services.addSignature(signedByA) val stx = bobNode.services.addSignature(signedByA)
@ -140,7 +147,7 @@ class ContractUpgradeFlowTest {
)) ))
val rpcA = startProxy(aliceNode, user) val rpcA = startProxy(aliceNode, user)
val rpcB = startProxy(bobNode, 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() mockNet.runNetwork()
handle.returnValue.getOrThrow() handle.returnValue.getOrThrow()
@ -202,7 +209,7 @@ class ContractUpgradeFlowTest {
@Test @Test
fun `upgrade Cash to v2`() { fun `upgrade Cash to v2`() {
// Create some cash. // 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 val result = aliceNode.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
val stx = result.getOrThrow().stx val stx = result.getOrThrow().stx

View File

@ -28,12 +28,11 @@ class FinalityFlowTests {
mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset")) mockNet = MockNetwork(cordappPackages = listOf("net.corda.finance.contracts.asset"))
val aliceNode = mockNet.createPartyNode(ALICE_NAME) val aliceNode = mockNet.createPartyNode(ALICE_NAME)
val bobNode = mockNet.createPartyNode(BOB_NAME) val bobNode = mockNet.createPartyNode(BOB_NAME)
mockNet.runNetwork()
aliceServices = aliceNode.services aliceServices = aliceNode.services
bobServices = bobNode.services bobServices = bobNode.services
alice = aliceNode.info.singleIdentity() alice = aliceNode.info.singleIdentity()
bob = bobNode.info.singleIdentity() bob = bobNode.info.singleIdentity()
notary = aliceServices.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
} }
@After @After

View File

@ -5,8 +5,9 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.testing.chooseIdentity
import net.corda.testing.node.network 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.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
@ -19,7 +20,7 @@ class ReceiveMultipleFlowTests {
val stringValue = "Thriller" val stringValue = "Thriller"
nodes[2].registerAnswer(AlgorithmDefinition::class, stringValue) 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() runNetwork()
val result = flow.resultFuture.getOrThrow() val result = flow.resultFuture.getOrThrow()
@ -36,7 +37,7 @@ class ReceiveMultipleFlowTests {
val value2 = 6.0 val value2 = 6.0
nodes[2].registerAnswer(ParallelAlgorithmList::class, value2) 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() runNetwork()
val data = flow.resultFuture.getOrThrow() val data = flow.resultFuture.getOrThrow()

View File

@ -10,10 +10,10 @@ import net.corda.core.utilities.sequence
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.chooseIdentity
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.singleIdentity
import net.corda.testing.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -44,10 +44,9 @@ class ResolveTransactionsFlowTest {
miniCorpNode = mockNet.createPartyNode(MINI_CORP.name) miniCorpNode = mockNet.createPartyNode(MINI_CORP.name)
megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) megaCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
miniCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java) miniCorpNode.internals.registerInitiatedFlow(TestResponseFlow::class.java)
mockNet.runNetwork() notary = mockNet.defaultNotaryIdentity
notary = notaryNode.services.getDefaultNotary() megaCorp = megaCorpNode.info.singleIdentity()
megaCorp = megaCorpNode.info.chooseIdentity() miniCorp = miniCorpNode.info.singleIdentity()
miniCorp = miniCorpNode.info.chooseIdentity()
} }
@After @After
@ -56,7 +55,6 @@ class ResolveTransactionsFlowTest {
} }
// DOCEND 3 // DOCEND 3
// DOCSTART 1 // DOCSTART 1
@Test @Test
fun `resolve from two hashes`() { fun `resolve from two hashes`() {

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

View File

@ -7,6 +7,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.TestDataVendingFlow import net.corda.core.flows.TestDataVendingFlow
import net.corda.core.identity.Party
import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchAttachmentsFlow
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
import net.corda.core.utilities.getOrThrow 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.internal.StartedNode
import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.utilities.currentDBSession 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.MockNetwork
import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.MockNodeParameters
import net.corda.testing.singleIdentity
import net.corda.testing.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -62,14 +66,16 @@ class AttachmentSerializationTest {
private lateinit var mockNet: MockNetwork private lateinit var mockNet: MockNetwork
private lateinit var server: StartedNode<MockNetwork.MockNode> private lateinit var server: StartedNode<MockNetwork.MockNode>
private lateinit var client: StartedNode<MockNetwork.MockNode> private lateinit var client: StartedNode<MockNetwork.MockNode>
private lateinit var serverIdentity: Party
@Before @Before
fun setUp() { fun setUp() {
mockNet = MockNetwork() mockNet = MockNetwork()
server = mockNet.createNode() server = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
client = mockNet.createNode() 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. client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client.
mockNet.runNetwork() mockNet.runNetwork()
serverIdentity = server.info.singleIdentity()
} }
@After @After
@ -91,9 +97,7 @@ class AttachmentSerializationTest {
private class ClientResult(internal val attachmentContent: String) private class ClientResult(internal val attachmentContent: String)
@InitiatingFlow @InitiatingFlow
private abstract class ClientLogic(server: StartedNode<*>) : FlowLogic<ClientResult>() { private abstract class ClientLogic(val serverIdentity: Party) : FlowLogic<ClientResult>() {
internal val server = server.info.chooseIdentity()
@Suspendable @Suspendable
internal fun communicate(serverSession: FlowSession) { internal fun communicate(serverSession: FlowSession) {
serverSession.sendAndReceive<String>("ping one").unwrap { assertEquals("pong", it) } serverSession.sendAndReceive<String>("ping one").unwrap { assertEquals("pong", it) }
@ -112,30 +116,30 @@ class AttachmentSerializationTest {
override val signers get() = throw UnsupportedOperationException() 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 @Suspendable
override fun getAttachmentContent(): String { override fun getAttachmentContent(): String {
val customAttachment = CustomAttachment(attachmentId, customContent) val customAttachment = CustomAttachment(attachmentId, customContent)
val session = initiateFlow(server) val session = initiateFlow(serverIdentity)
communicate(session) communicate(session)
return customAttachment.customContent 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 @Suspendable
override fun getAttachmentContent(): String { override fun getAttachmentContent(): String {
val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!! val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!!
val session = initiateFlow(server) val session = initiateFlow(serverIdentity)
communicate(session) communicate(session)
return localAttachment.extractContent() 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 @Suspendable
override fun getAttachmentContent(): String { override fun getAttachmentContent(): String {
val serverSession = initiateFlow(server) val serverSession = initiateFlow(serverIdentity)
val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), serverSession)).downloaded val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), serverSession)).downloaded
serverSession.send(FetchDataFlow.Request.End) serverSession.send(FetchDataFlow.Request.End)
communicate(serverSession) communicate(serverSession)
@ -166,14 +170,14 @@ class AttachmentSerializationTest {
@Test @Test
fun `custom (and non-persisted) attachment should be saved in checkpoint`() { fun `custom (and non-persisted) attachment should be saved in checkpoint`() {
val attachmentId = SecureHash.sha256("any old data") val attachmentId = SecureHash.sha256("any old data")
launchFlow(CustomAttachmentLogic(server, attachmentId, "custom"), 1) launchFlow(CustomAttachmentLogic(serverIdentity, attachmentId, "custom"), 1)
assertEquals("custom", rebootClientAndGetAttachmentContent()) assertEquals("custom", rebootClientAndGetAttachmentContent())
} }
@Test @Test
fun `custom attachment should be saved in checkpoint even if its data was persisted`() { fun `custom attachment should be saved in checkpoint even if its data was persisted`() {
val attachmentId = client.saveAttachment("genuine") 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. client.hackAttachment(attachmentId, "hacked") // Should not be reloaded, checkAttachmentsOnLoad may cause next line to blow up if client attempts it.
assertEquals("custom", rebootClientAndGetAttachmentContent()) assertEquals("custom", rebootClientAndGetAttachmentContent())
} }
@ -182,7 +186,7 @@ class AttachmentSerializationTest {
fun `only the hash of a regular attachment should be saved in checkpoint`() { fun `only the hash of a regular attachment should be saved in checkpoint`() {
val attachmentId = client.saveAttachment("genuine") val attachmentId = client.saveAttachment("genuine")
client.attachments.checkAttachmentsOnLoad = false // Cached by AttachmentImpl. client.attachments.checkAttachmentsOnLoad = false // Cached by AttachmentImpl.
launchFlow(OpenAttachmentLogic(server, attachmentId), 1) launchFlow(OpenAttachmentLogic(serverIdentity, attachmentId), 1)
client.hackAttachment(attachmentId, "hacked") client.hackAttachment(attachmentId, "hacked")
assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) // Pass in false to allow non-genuine data to be loaded. assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) // Pass in false to allow non-genuine data to be loaded.
} }
@ -190,7 +194,7 @@ class AttachmentSerializationTest {
@Test @Test
fun `only the hash of a FetchAttachmentsFlow attachment should be saved in checkpoint`() { fun `only the hash of a FetchAttachmentsFlow attachment should be saved in checkpoint`() {
val attachmentId = server.saveAttachment("genuine") val attachmentId = server.saveAttachment("genuine")
launchFlow(FetchAttachmentLogic(server, attachmentId), 2, sendData = true) launchFlow(FetchAttachmentLogic(serverIdentity, attachmentId), 2, sendData = true)
client.hackAttachment(attachmentId, "hacked") client.hackAttachment(attachmentId, "hacked")
assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) assertEquals("hacked", rebootClientAndGetAttachmentContent(false))
} }

View File

@ -50,8 +50,8 @@ class TransactionSerializationTests {
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY) 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 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 megaCorpServices = MockServices(listOf("net.corda.core.serialization"), MEGA_CORP.name, MEGA_CORP_KEY)
val notaryServices = MockServices(listOf("net.corda.core.serialization"), DUMMY_NOTARY_KEY) val notaryServices = MockServices(listOf("net.corda.core.serialization"), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
lateinit var tx: TransactionBuilder lateinit var tx: TransactionBuilder
@Before @Before

View File

@ -2,9 +2,13 @@ package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AbstractParty 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.contracts.DummyContract
import net.corda.testing.dummyCommand
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.singleIdentity
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -18,6 +22,7 @@ class LedgerTransactionQueryTests {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val services: MockServices = MockServices() private val services: MockServices = MockServices()
private val identity: Party = services.myInfo.singleIdentity()
@Before @Before
fun setup() { fun setup() {
@ -66,8 +71,8 @@ class LedgerTransactionQueryTests {
tx.addInputState(makeDummyStateAndRef(i.toString())) tx.addInputState(makeDummyStateAndRef(i.toString()))
tx.addOutputState(makeDummyState(i), DummyContract.PROGRAM_ID) tx.addOutputState(makeDummyState(i), DummyContract.PROGRAM_ID)
tx.addOutputState(makeDummyState(i.toString()), DummyContract.PROGRAM_ID) tx.addOutputState(makeDummyState(i.toString()), DummyContract.PROGRAM_ID)
tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.chooseIdentity().owningKey)) tx.addCommand(Commands.Cmd1(i), listOf(identity.owningKey))
tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.chooseIdentity().owningKey)) tx.addCommand(Commands.Cmd2(i), listOf(identity.owningKey))
} }
return tx.toLedgerTransaction(services) return tx.toLedgerTransaction(services)
} }

View File

@ -9,7 +9,9 @@ import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.ledger import net.corda.testing.ledger
import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@ -17,6 +19,9 @@ import java.time.temporal.ChronoUnit
val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock" val TEST_TIMELOCK_ID = "net.corda.core.transactions.TransactionEncumbranceTests\$DummyTimeLock"
class TransactionEncumbranceTests { class TransactionEncumbranceTests {
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
val defaultIssuer = MEGA_CORP.ref(1) val defaultIssuer = MEGA_CORP.ref(1)
val state = Cash.State( val state = Cash.State(

View File

@ -1,12 +1,13 @@
Building a CorDapp CorDapps
================== ========
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
cordapp-overview cordapp-overview
writing-cordapps writing-a-cordapp
cordapp-build-systems cordapp-build-systems
building-against-master
corda-api corda-api
flow-cookbook flow-cookbook
cheat-sheet cheat-sheet

View File

@ -8,6 +8,9 @@ UNRELEASED
---------- ----------
* ``ConfigUtilities`` now read system properties for a node. This allow to specify data source properties at runtime. * ``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. * ``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``. * ``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 * 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. 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: .. _changelog_v1:
Release 1.0 Release 1.0

View File

@ -45,8 +45,7 @@ Simple Notary configuration file.
} }
useHTTPS : false useHTTPS : false
devMode : true devMode : true
// Certificate signing service will be hosted by R3 in the near future. compatibilityZoneURL : "https://cz.corda.net"
//certificateSigningService : "https://testnet.certificate.corda.net"
Fields 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 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. 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 :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 SSL certificate. (See :doc:`permissioning` for more information.) 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`` :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" ]`` only. e.g. ``jvmArgs = [ "-Xmx220m", "-Xms220m", "-XX:+UseG1GC" ]``

View File

@ -6,9 +6,9 @@ Corda nodes
deploying-a-node deploying-a-node
running-a-node running-a-node
corda-configuration-file
clientrpc clientrpc
shell shell
node-database node-database
node-administration node-administration
corda-configuration-file
out-of-process-verification out-of-process-verification

View File

@ -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 .. contents::
please read :doc:`cordapp-overview`. To learn about writing a Cordapp as a developer please read :doc:`writing-cordapps`.
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 For example, if a Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons``, then the Cordapp
is a semi-fat JAR that contains all of its own dependencies *except* the Corda core libraries and other Cordapps. JAR will contain:
For example if your Cordapp depends on ``corda-core``, ``your-other-cordapp`` and ``apache-commons`` then the Cordapp * All classes and resources from the ``apache-commons`` JAR and its dependencies
JAR will contain all classes and resources from the ``apache-commons`` JAR and its dependencies and *nothing* from the * *Nothing* from the other two JARs
other two JARs.
.. note:: The rest of this tutorial assumes you are using ``gradle``, the ``cordapp`` plugin and have forked from Build tools
one of our cordapp templates. -----------
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 Setting your dependencies
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.
Choosing your Corda version Choosing your Corda version
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -49,11 +32,15 @@ The following two lines of the ``build.gradle`` file define the Corda version us
.. sourcecode:: groovy .. sourcecode:: groovy
ext.corda_release_version = '0.13.0' ext.corda_release_version = '1.0.0'
ext.corda_gradle_plugins_version = '0.13.3' 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 In this case, our CorDapp will use:
can find the latest published version of both here: https://bintray.com/r3/corda.
* 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 ``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 ``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`. 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 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. in Gradle. See the example below, specifically the ``apache-commons`` include.
For further information about managing dependencies, see For further information about managing dependencies, see
`the Gradle docs <https://docs.gradle.org/current/userguide/dependency_management.html>`_. `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 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; is already correctly configured and this is for reference only;
.. container:: codeset .. 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" testCompile "net.corda:corda-test-utils:$corda_release_version"
// Corda Plugins: dependent flows and services // 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" cordapp "net.corda:bank-of-corda-demo:1.0"
// Some other dependencies // 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" 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.

View File

@ -1,25 +1,30 @@
What is a CorDapp? What is a CorDapp?
================== ==================
Corda is a platform. Its functionality is extended by developers through the writing of Corda distributed CorDapps (Corda Distributed Applications) are distributed applications that run on the Corda platform. The goal of a
applications (CorDapps). CorDapps are installed at the level of the individual node, rather than on the network CorDapp is to allow nodes to reach agreement on updates to the ledger. They achieve this goal by defining flows that
itself. Corda node owners can invoke through RPC calls:
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.
.. image:: resources/node-diagram.png .. image:: resources/node-diagram.png
CorDapp developers will usually define not only these flows, but also any states and contracts that these flows use. CorDapps are made up of the following key components:
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 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 Each CorDapp is installed at the level of the individual node, rather than on the network itself. For example, a node
* Contracts owner may choose to install the Bond Trading CorDapp, with the following components:
* Flows
* Web APIs and static web content * A ``BondState``, used to represent bonds as shared facts on the ledger
* Services * 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.

View File

@ -1,97 +1,141 @@
Deploying a node Deploying a node
================ ================
Using Gradle to build nodes Node structure
--------------------------- --------------
Nodes are usually built using a Gradle task. The canonical Gradle file for building nodes is the one used by the Each Corda node has the following structure:
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.
Cordform is the local node deployment system for CorDapps. The nodes generated are intended for experimenting, .. sourcecode:: none
debugging, and testing node configurations, but not for production or testnet deployment.
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 .. sourcecode:: groovy
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes" directory "./build/nodes"
networkMap "O=Controller,L=London,C=GB"
node { 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] notary = [validating : true]
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
webPort 10004 // No webport property, so no webserver will be created.
cordapps = [] h2Port 10004
sshdPort 22
// Includes the corda-finance CorDapp on our node.
cordapps = ["net.corda:corda-finance:$corda_release_version"]
} }
node { node {
name "CN=NodeA,O=NodeA,L=London,C=UK" name "O=PartyA,L=London,C=GB"
p2pPort 10005 advertisedServices = []
rpcPort 10006 p2pPort 10005
webPort 10007 rpcPort 10006
cordapps = [] webPort 10007
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] 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 { node {
name "CN=NodeB,O=NodeB,L=New York,C=US" name "O=PartyB,L=New York,C=US"
p2pPort 10008 advertisedServices = []
rpcPort 10009 p2pPort 10009
webPort 10010 rpcPort 10010
cordapps = [] webPort 10011
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]] h2Port 10012
} sshdPort 22
// Example of explicit addresses being used. cordapps = ["net.corda:corda-finance:$corda_release_version"]
node { // Grants user1 the ability to start the MyFlow flow.
name "CN=NodeC,O=NodeC,L=Paris,C=FR" rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
p2pAddress "localhost:10011"
rpcAddress "localhost:10012"
webAddress "localhost:10013"
cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
} }
} }
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! .. 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: Running deployNodes
-------------------
.. sourcecode:: groovy 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:
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:
* Unix/Mac OSX: ``./gradlew deployNodes`` * Unix/Mac OSX: ``./gradlew deployNodes``
* Windows: ``gradlew.bat deployNodes`` * Windows: ``gradlew.bat deployNodes``
After the build process has finished, you will find the newly-built nodes under ``kotlin-source/build/nodes``. There This will create the nodes in the ``build/nodes`` folder.
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.
.. note:: During the build process each node generates a NodeInfo file which is written in its own root directory, .. 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 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 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>`.

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.TransactionSignature;
import net.corda.core.flows.*; import net.corda.core.flows.*;
import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.CordaX500Name;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.identity.PartyAndCertificate;
import net.corda.core.internal.FetchDataFlow; import net.corda.core.internal.FetchDataFlow;
import net.corda.core.node.services.Vault; import net.corda.core.node.services.Vault;
import net.corda.core.node.services.Vault.Page; import net.corda.core.node.services.Vault.Page;
@ -401,8 +402,8 @@ public class FlowCookbookJava {
// DOCEND 29 // DOCEND 29
// We can also sign the transaction using a different public key: // We can also sign the transaction using a different public key:
// DOCSTART 30 // DOCSTART 30
PublicKey otherKey = getServiceHub().getKeyManagementService().freshKey(); PartyAndCertificate otherIdentity = getServiceHub().getKeyManagementService().freshKeyAndCert(getOurIdentityAndCert(), false);
SignedTransaction onceSignedTx2 = getServiceHub().signInitialTransaction(txBuilder, otherKey); SignedTransaction onceSignedTx2 = getServiceHub().signInitialTransaction(txBuilder, otherIdentity.getOwningKey());
// DOCEND 30 // DOCEND 30
// If instead this was a ``SignedTransaction`` that we'd received // If instead this was a ``SignedTransaction`` that we'd received
@ -412,9 +413,9 @@ public class FlowCookbookJava {
SignedTransaction twiceSignedTx = getServiceHub().addSignature(onceSignedTx); SignedTransaction twiceSignedTx = getServiceHub().addSignature(onceSignedTx);
// DOCEND 38 // DOCEND 38
// Or, if we wanted to use a different public key: // Or, if we wanted to use a different public key:
PublicKey otherKey2 = getServiceHub().getKeyManagementService().freshKey(); PartyAndCertificate otherIdentity2 = getServiceHub().getKeyManagementService().freshKeyAndCert(getOurIdentityAndCert(), false);
// DOCSTART 39 // DOCSTART 39
SignedTransaction twiceSignedTx2 = getServiceHub().addSignature(onceSignedTx, otherKey2); SignedTransaction twiceSignedTx2 = getServiceHub().addSignature(onceSignedTx, otherIdentity2.getOwningKey());
// DOCEND 39 // DOCEND 39
// We can also generate a signature over the transaction without // We can also generate a signature over the transaction without
@ -428,7 +429,7 @@ public class FlowCookbookJava {
// DOCEND 40 // DOCEND 40
// And again, if we wanted to use a different public key: // And again, if we wanted to use a different public key:
// DOCSTART 41 // DOCSTART 41
TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherKey2); TransactionSignature sig2 = getServiceHub().createSignature(onceSignedTx, otherIdentity2.getOwningKey());
// DOCEND 41 // DOCEND 41
/*---------------------------- /*----------------------------

View File

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

View File

@ -3,13 +3,15 @@ package net.corda.docs.java.tutorial.helloworld;
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.contracts.Command; 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.flows.*;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction; import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder; import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker; import net.corda.core.utilities.ProgressTracker;
import static net.corda.docs.java.tutorial.helloworld.TemplateContract.TEMPLATE_CONTRACT_ID;
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
public class IOUFlow extends FlowLogic<Void> { 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. // We retrieve the notary identity from the network map.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); 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. // We create the transaction components.
IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty); IOUState outputState = new IOUState(iouValue, getOurIdentity(), otherParty);
String outputContract = IOUContract.class.getName(); CommandData cmdType = new TemplateContract.Commands.Action();
StateAndContract outputContractAndState = new StateAndContract(outputState, outputContract); Command cmd = new Command<>(cmdType, getOurIdentity().getOwningKey());
Command cmd = new Command<>(new IOUContract.Create(), getOurIdentity().getOwningKey());
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd); final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
// Verifying the transaction. .addCommand(cmd);
txBuilder.verify(getServiceHub());
// Signing the transaction. // Signing the transaction.
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder); final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);

View File

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

View File

@ -6,6 +6,8 @@ import net.corda.core.utilities.OpaqueBytes;
import net.corda.finance.contracts.ICommercialPaperState; import net.corda.finance.contracts.ICommercialPaperState;
import net.corda.finance.contracts.JavaCommercialPaper; import net.corda.finance.contracts.JavaCommercialPaper;
import net.corda.finance.contracts.asset.Cash; import net.corda.finance.contracts.asset.Cash;
import net.corda.testing.SerializationEnvironmentRule;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
@ -19,6 +21,8 @@ import static net.corda.testing.NodeTestUtils.transaction;
import static net.corda.testing.TestConstants.*; import static net.corda.testing.TestConstants.*;
public class CommercialPaperTest { public class CommercialPaperTest {
@Rule
public final SerializationEnvironmentRule testSerialization = new SerializationEnvironmentRule();
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{123}); private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{123});
// DOCSTART 1 // DOCSTART 1

View File

@ -13,7 +13,6 @@ import java.util.List;
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand; import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat; import static net.corda.core.contracts.ContractsDSL.requireThat;
// DOCEND 01
public class IOUContract implements Contract { public class IOUContract implements Contract {
// Our Create command. // Our Create command.
@ -22,7 +21,7 @@ public class IOUContract implements Contract {
@Override @Override
public void verify(LedgerTransaction tx) { 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 -> { requireThat(check -> {
// Constraints on the shape of the transaction. // 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 IOU's value must be non-negative.", out.getValue() > 0);
check.using("The lender and the borrower cannot be the same entity.", lender != borrower); check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
// DOCSTART 02
// Constraints on the signers. // Constraints on the signers.
final List<PublicKey> signers = command.getSigners(); final List<PublicKey> signers = command.getSigners();
check.using("There must be two signers.", signers.size() == 2); check.using("There must be two signers.", signers.size() == 2);
check.using("The borrower and lender must be signers.", signers.containsAll( check.using("The borrower and lender must be signers.", signers.containsAll(
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey()))); ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
// DOCEND 02
return null; return null;
}); });
} }
} }
// DOCEND 01

View File

@ -48,7 +48,7 @@ fun main(args: Array<String>) {
startFlow<CashExitFlow>(), startFlow<CashExitFlow>(),
invokeRpc(CordaRPCOps::nodeInfo) 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() val node = startNode(providedName = ALICE.name, rpcUsers = listOf(user)).get()
// END 1 // END 1
@ -96,7 +96,6 @@ fun main(args: Array<String>) {
graph.display() graph.display()
} }
} }
waitForAllNodesToFinish()
// END 5 // END 5
} }
} }

View File

@ -9,6 +9,7 @@ import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.FetchDataFlow import net.corda.core.internal.FetchDataFlow
import net.corda.core.node.services.Vault.Page import net.corda.core.node.services.Vault.Page
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy
@ -387,8 +388,8 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty:
// DOCEND 29 // DOCEND 29
// We can also sign the transaction using a different public key: // We can also sign the transaction using a different public key:
// DOCSTART 30 // DOCSTART 30
val otherKey: PublicKey = serviceHub.keyManagementService.freshKey() val otherIdentity: PartyAndCertificate = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false)
val onceSignedTx2: SignedTransaction = serviceHub.signInitialTransaction(txBuilder, otherKey) val onceSignedTx2: SignedTransaction = serviceHub.signInitialTransaction(txBuilder, otherIdentity.owningKey)
// DOCEND 30 // DOCEND 30
// If instead this was a ``SignedTransaction`` that we'd received // 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) val twiceSignedTx: SignedTransaction = serviceHub.addSignature(onceSignedTx)
// DOCEND 38 // DOCEND 38
// Or, if we wanted to use a different public key: // 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 // DOCSTART 39
val twiceSignedTx2: SignedTransaction = serviceHub.addSignature(onceSignedTx, otherKey2) val twiceSignedTx2: SignedTransaction = serviceHub.addSignature(onceSignedTx, otherIdentity2.owningKey)
// DOCEND 39 // DOCEND 39
// We can also generate a signature over the transaction without // 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 // DOCEND 40
// And again, if we wanted to use a different public key: // And again, if we wanted to use a different public key:
// DOCSTART 41 // DOCSTART 41
val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherKey2) val sig2: TransactionSignature = serviceHub.createSignature(onceSignedTx, otherIdentity2.owningKey)
// DOCEND 41 // DOCEND 41
// In practice, however, the process of gathering every signature // In practice, however, the process of gathering every signature

View File

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

View File

@ -3,7 +3,6 @@ package net.corda.docs.tutorial.helloworld
// DOCSTART 01 // DOCSTART 01
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow 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.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import kotlin.reflect.jvm.jvmName
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
@ -27,25 +25,19 @@ class IOUFlow(val iouValue: Int,
// We retrieve the notary identity from the network map. // We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0] val notary = serviceHub.networkMapCache.notaryIdentities[0]
// We create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
// We create the transaction components. // We create the transaction components.
val outputState = IOUState(iouValue, ourIdentity, otherParty) val outputState = IOUState(iouValue, ourIdentity, otherParty)
val outputContract = IOUContract::class.jvmName val cmd = Command(TemplateContract.Commands.Action(), ourIdentity.owningKey)
val outputContractAndState = StateAndContract(outputState, outputContract)
val cmd = Command(IOUContract.Create(), ourIdentity.owningKey)
// We add the items to the builder. // We create a transaction builder and add the components.
txBuilder.withItems(outputContractAndState, cmd) val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, TEMPLATE_CONTRACT_ID)
.addCommand(cmd)
// Verifying the transaction. // We sign the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder) val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction. // We finalise the transaction.
subFlow(FinalityFlow(signedTx)) subFlow(FinalityFlow(signedTx))
} }
} }

View File

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

View File

@ -19,6 +19,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MessagingServiceSpy import net.corda.testing.node.MessagingServiceSpy
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.setMessagingServiceSpy import net.corda.testing.node.setMessagingServiceSpy
import net.corda.testing.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -67,10 +68,7 @@ class TutorialMockNetwork {
mockNet = MockNetwork() mockNet = MockNetwork()
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeB.registerInitiatedFlow(FlowB::class.java) nodeB.registerInitiatedFlow(FlowB::class.java)
mockNet.runNetwork()
} }
@After @After

View File

@ -8,11 +8,15 @@ import net.corda.finance.contracts.CommercialPaper
import net.corda.finance.contracts.ICommercialPaperState 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.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.ownedBy
import net.corda.testing.* import net.corda.testing.*
import org.junit.Rule
import org.junit.Test import org.junit.Test
class CommercialPaperTest { class CommercialPaperTest {
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
// DOCSTART 1 // DOCSTART 1
fun getPaper(): ICommercialPaperState = CommercialPaper.State( fun getPaper(): ICommercialPaperState = CommercialPaper.State(
issuance = MEGA_CORP.ref(123), issuance = MEGA_CORP.ref(123),

View File

@ -6,7 +6,6 @@ import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
// DOCEND 01
class IOUContract : Contract { class IOUContract : Contract {
// Our Create command. // Our Create command.
@ -25,12 +24,11 @@ class IOUContract : Contract {
"The IOU's value must be non-negative." using (out.value > 0) "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) "The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
// DOCSTART 02
// Constraints on the signers. // Constraints on the signers.
"There must be two signers." using (command.signers.toSet().size == 2) "There must be two signers." using (command.signers.toSet().size == 2)
"The borrower and lender must be signers." using (command.signers.containsAll(listOf( "The borrower and lender must be signers." using (command.signers.containsAll(listOf(
out.borrower.owningKey, out.lender.owningKey))) out.borrower.owningKey, out.lender.owningKey)))
// DOCEND 02
} }
} }
} }
// DOCEND 01

View File

@ -2,17 +2,15 @@ package net.corda.docs
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.packageName
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.* import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.startFlow
import org.junit.After import org.junit.After
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
@ -27,17 +25,11 @@ class CustomVaultQueryTest {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(threadPerNode = true, mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.docs"))
cordappPackages = listOf(
"net.corda.finance.contracts.asset",
CashSchemaV1::class.packageName,
"net.corda.docs"
)
)
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java)
notary = nodeA.services.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
} }
@After @After

View File

@ -1,18 +1,16 @@
package net.corda.docs package net.corda.docs
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.packageName
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.* import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.schemas.CashSchemaV1
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.getDefaultNotary
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.startFlow
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -26,11 +24,11 @@ class FxTransactionBuildTutorialTest {
@Before @Before
fun setup() { 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() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java)
notary = nodeA.services.getDefaultNotary() notary = mockNet.defaultNotaryIdentity
} }
@After @After

View File

@ -10,8 +10,7 @@ import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.api.StartedNodeServices
import net.corda.testing.ALICE_NAME import net.corda.testing.*
import net.corda.testing.BOB_NAME
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before

View File

@ -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. 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 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-index>`. You'll want to refer to the :doc:`API docs <api-index>`, 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. :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 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/>`_.

View File

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

View File

@ -6,35 +6,42 @@
Writing the flow Writing the flow
================ ================
A flow describes the sequence of steps for agreeing a specific ledger update. By installing new flows on our node, we A flow encodes a sequence of steps that a node can run to achieve a specific ledger update. By installing new flows on
allow the node to handle new business processes. Our flow will allow a node to issue an ``IOUState`` onto the ledger. 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 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 The process of creating and applying this transaction to a ledger will be conducted by the IOU's lender, and will
2. Verify the transaction require the following steps:
3. Sign the transaction ourselves
4. Record the transaction in our vault 1. Building the transaction proposal for the issuance of a new IOU onto a ledger
5. Send the transaction to the IOU's lender so that they can record it too 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 Subflows
^^^^^^^^ ^^^^^^^^
Although our flow requirements look complex, we can delegate to existing flows to handle many of these tasks. A flow Tasks like recording a transaction or sending a transaction to a counterparty are very common in Corda. Instead of
that is invoked within the context of a larger flow to handle a repeatable task is called a *subflow*. 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``. In our case, we can automate steps 3 and 4 of the IOU issuance flow using ``FinalityFlow``.
All we need to do is write the steps to handle the creation and signing of the proposed transaction.
FlowLogic FlowLogic
--------- ---------
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding All flows must subclass ``FlowLogic``. You then define the steps taken by the flow by overriding ``FlowLogic.call``.
``FlowLogic.call``.
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the template, and Let's define our ``IOUFlow`` in either ``TemplateFlow.java`` or ``App.kt``. Delete both the existing flows in the
replace them with the following: template, and replace them with the following:
.. container:: codeset .. container:: codeset
@ -48,108 +55,105 @@ replace them with the following:
:start-after: DOCSTART 01 :start-after: DOCSTART 01
:end-before: DOCEND 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 ``FlowLogic`` subclasses can optionally have constructor parameters, which can be used as arguments to
by running the flow ``FlowLogic.call``. In our case, we have two:
* ``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 * ``iouValue``, which is the value of the IOU being issued
disk when it encounters a long-running operation, allowing your node to move on to running other flows. Forgetting * ``otherParty``, the IOU's borrower (the node running the flow is the lender)
this annotation out will lead to some very weird error messages
* There are also a few more annotations, on the ``FlowLogic`` subclass itself: ``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 * ``@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 * ``@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 Let's walk through the steps of ``FlowLogic.call`` itself. This is where we actually describe the procedure for
tracker is required for the node shell to establish when the flow has ended 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 .. note::
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
You can see that the notary's identity is being retrieved from the node's ``ServiceHub``. Whenever we need Whenever we need information within a flow - whether it's about our own node's identity, the node's local storage,
information within a flow - whether it's about our own node, its contents, or the rest of the network - we use the or the rest of the network - we generally obtain it via the node's ``ServiceHub``.
node's ``ServiceHub``. In particular, ``ServiceHub.networkMapCache`` provides information about the other nodes on the
network and the services that they offer.
Building the transaction Building the transaction
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
We'll build our transaction proposal in two steps: We'll build our transaction proposal in two steps:
* Creating a transaction builder * Creating the transaction's components
* Adding the desired items to the builder * Adding these components to a transaction 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.
Transaction items Transaction items
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
Now that we have our ``TransactionBuilder``, we need to add the desired items. Remember that we're trying to build Our transaction will have the following structure:
the following transaction:
.. image:: resources/simple-tutorial-transaction.png .. image:: resources/simple-tutorial-transaction.png
:scale: 15% :scale: 15%
:align: center :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 * An ``Action`` command listing the IOU's lender as a signer
* A ``Create`` 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 We've already talked about the ``IOUState``, but we haven't looked at commands yet. Commands serve two functions:
in the transaction makes us one of the transaction's required signers.
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 IOUs borrower and lender
* ``StateAndContract`` or ``TransactionState`` objects, which are added to the builder as output states Each ``Command`` contains a command type plus a list of public keys. For now, we use the pre-defined
* ``StateAndRef`` objects (references to the outputs of previous transactions), which are added to the builder as input ``TemplateContract.Action`` as our command type, and we list the lender as the only public key. This means that for
state references the transaction to be valid, the lender is required to sign the transaction.
* ``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
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 Once we have the ``TransactionBuilder``, we add our components:
^^^^^^^^^^^^^^^^^^^^^^^^^
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.
If the verification fails, we have built an invalid transaction. Our flow will then end, throwing a * The command is added directly using ``TransactionBuilder.addCommand``
``TransactionVerificationException``. * 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 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 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 We sign the transaction using ``ServiceHub.toSignedTransaction``, which returns a ``SignedTransaction``. A
transaction itself with a list of signatures over that transaction. ``SignedTransaction`` is an object that pairs a transaction with a list of signatures over that transaction.
Finalising the 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 We now have a valid signed transaction. All that's left to do is to have it recorded by all the relevant parties. By
relevant parties. By doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process doing so, it will become a permanent part of the ledger. As discussed, we'll handle this process automatically using a
automatically using a built-in flow called ``FinalityFlow``: built-in flow called ``FinalityFlow``. ``FinalityFlow`` completely automates the process of:
``FinalityFlow`` completely automates the process of:
* Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window) * Notarising the transaction if required (i.e. if the transaction contains inputs and/or a time-window)
* Recording it in our vault * Recording it in our vault
* Sending it to the other participants (i.e. the lender) for them to record as well * 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 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 Our flow, and our CorDapp, are now ready! We have now defined a flow that we can start on our node to completely
ledger. The final step is to spin up some nodes and test our CorDapp. automate the process of issuing an IOU onto the ledger. All that's left is to spin up some nodes and test our CorDapp.

View File

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

View File

@ -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 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 :doc:`your first CorDapp <tutorial-cordapp>`, and you're familiar with Corda's :doc:`key concepts <key-concepts>`. What
comes next? 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 If you're a developer, the next step is to write your own CorDapp. CorDapps are plugins that are installed on one or
installed on one or more Corda nodes, and gives them the ability to conduct some new process - anything from 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. issuing a debt instrument to making a restaurant booking.
Our use-case 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 Our CorDapp will model IOUs on-ledger. An IOU short for “I O(we) (yo)U” 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 another person a given amount of money. Clearly this is sensitive information that we'd only want to communicate on
communicate on a need-to-know basis. This is one of the areas where Corda excels - allowing a small set of parties to a need-to-know basis between the lender and the borrower. Fortunately, this is one of the areas where Corda excels.
agree on a fact without needing to share this fact with everyone else on the network, as you do with most other Corda makes it easy to allow a small set of parties to agree on a shared fact without needing to share this fact with
blockchain platforms. 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 * **States**, the shared facts that Corda nodes reach consensus over and are then stored on the ledger
* **One or more contracts** the rules governing how these states can evolve over time * **Flows**, which encapsulate the procedure for carrying out a specific ledger update
* **One or more flows** the step-by-step process for carrying out a 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 IOUState
^^^^^ ^^^^^^^^^^^^
The states will be IOUStates, with each instance representing a single IOU. We can visualize an IOUState as follows: 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 .. image:: resources/tutorial-state.png
:scale: 25% :scale: 25%
:align: center :align: center
Contract The IOUFlow
^^^^^^^^ ^^^^^^^^^^^
Our contract will be the IOUContract, imposing rules on the evolution of IOUs over time: 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:
* 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:
.. image:: resources/simple-tutorial-flow.png .. image:: resources/simple-tutorial-flow.png
:scale: 25% :scale: 25%
:align: center :align: center
In traditional distributed ledger systems, where all data is broadcast to every network participant, you dont even In traditional distributed ledger systems, where all data is broadcast to every network participant, you dont need to
think about this step you simply package up your ledger update and send it out into the world. But in Corda, where think about data flows you simply package up your ledger update and send it to everyone else on the network. But in
privacy is a core focus, flows are used to carefully control who sees what during the process of agreeing a Corda, where privacy is a core focus, flows allow us to carefully control who sees what during the process of
ledger update. agreeing a ledger update.
Progress so far 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.

View File

@ -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. 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 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 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 .. 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: We can run this ``deployNodes`` task using Gradle. For each node definition, Gradle will:
* Package the project's source files into a CorDapp jar * 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" 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 This single command will cause PartyA and PartyB to automatically agree an IOU. This is one of the great advantages of
in the vaults of both PartyA and PartyB. 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`` If the flow worked, it should have recorded a new IOU in the vaults of both PartyA and PartyB. Let's check.
will display a list of the available commands. We can examine the contents of a node's vault by running:
We can check the contents of each node's vault by running:
.. container:: codeset .. container:: codeset
@ -166,31 +160,23 @@ The vaults of PartyA and PartyB should both display the following output:
stateTypes: "UNCONSUMED" stateTypes: "UNCONSUMED"
otherResults: [] otherResults: []
This is the transaction issuing our ``IOUState`` onto a ledger.
Conclusion Conclusion
---------- ----------
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Our CorDapp is made up of two key
CorDapp is made up of three key parts: parts:
* The ``IOUState``, representing IOUs on the ledger * 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 * 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 Next steps
---------- ----------
There are a number of improvements we could make to this CorDapp: 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 * We chould add unit tests, using the contract-test and flow-test frameworks
``IOUState`` * We chould change ``IOUState.value`` from an integer to a proper amount of a given currency
* 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 could add an API, to make it easier to interact with the CorDapp * 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 But for now, the biggest priority is to add an ``IOUContract`` imposing constraints on the evolution of each
CorDapps. You can find a list of sample CorDapps `here <https://www.corda.net/samples/>`_. ``IOUState`` over time. This will be the focus of our next tutorial.
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>`_.

View File

@ -12,8 +12,8 @@ represent an IOU.
The ContractState interface The ContractState interface
--------------------------- ---------------------------
In Corda, any JVM class that implements the ``ContractState`` interface is a valid state. ``ContractState`` is A Corda state is any instance of a class that implements the ``ContractState`` interface. The ``ContractState``
defined as follows: interface is defined as follows:
.. container:: codeset .. container:: codeset
@ -24,6 +24,7 @@ defined as follows:
val participants: List<AbstractParty> 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 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. 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 `getting started guide <https://kotlinlang.org/docs/tutorials/>`_, and a series of
`Kotlin Koans <https://kotlinlang.org/docs/tutorials/koans.html>`_. `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 We can see that the ``ContractState`` interface has a single field, ``participants``. ``participants`` is a list of the
the entities for which this state is relevant. 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 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 .. note::
``LinearState`` and ``OwnableState``. See :doc:`api-states` for more information.
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 Modelling IOUs
-------------- --------------
How should we define the ``IOUState`` representing IOUs on the ledger? Beyond implementing the ``ContractState`` 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: 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 lender of the IOU
* The borrower 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 There are many more fields you could include, such as the IOU's currency, but let's ignore those for now. Adding them
you wish to add them later, its as simple as adding an additional property to your class definition. later is often as simple as adding an additional property to your class definition.
Defining IOUState Defining IOUState
----------------- -----------------
Let's open ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and update ``TemplateState`` to Let's get started by opening ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and updating
define an ``IOUState``: ``TemplateState`` to define an ``IOUState``:
.. container:: codeset .. 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``. 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 renamed the ``TemplateState`` class to ``IOUState``
* We've added properties for ``value``, ``lender`` and ``borrower`` (along with any getters and setters in Java): * 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) * ``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 * ``lender`` and ``borrower`` are of type ``Party``
the network.
* ``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`` * 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 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 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 JVM classes that implement the ``ContractState`` interface. They can have any additional properties and Corda are simply classes that implement the ``ContractState`` interface. They can have any additional properties and
methods you like. 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.

View File

@ -7,35 +7,42 @@
The CorDapp Template The CorDapp Template
==================== ====================
When writing a new CorDapp, youll generally want to base it on the When writing a new CorDapp, youll generally want to base it on the standard templates:
`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.
Note that there's no need to download and install Corda itself. As long as you're working from a stable Milestone * The `Java Cordapp Template <https://github.com/corda/cordapp-template-java>`_
branch, the required libraries will be downloaded automatically from an online repository. * 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 The Cordapp templates provide the required boilerplate for developing a CorDapp, and allow you to quickly deploy your
`here <https://docs.corda.net/tutorial-cordapp.html#using-a-snapshot-release>`_. 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 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 .. code-block:: bash
# Clone the template from GitHub:
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
*or* *or*
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin 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 Template structure
------------------ ------------------
We can write our CorDapp in either Java or Kotlin, and will be providing the code in both languages throughout. To The template has a number of files, but we can ignore most of them. To implement our IOU CorDapp in Java, we'll only
implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin, we'll simply be modifying the need to modify two files. For Kotlin, we'll simply be modifying the ``App.kt`` file:
``App.kt`` file:
.. container:: codeset .. container:: codeset
@ -44,23 +51,25 @@ implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin,
// 1. The state // 1. The state
src/main/java/com/template/TemplateState.java src/main/java/com/template/TemplateState.java
// 2. The contract // 2. The flow
src/main/java/com/template/TemplateContract.java
// 3. The flow
src/main/java/com/template/TemplateFlow.java src/main/java/com/template/TemplateFlow.java
.. code-block:: kotlin .. code-block:: kotlin
src/main/kotlin/com/template/App.kt 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`` * Java:
* Kotlin: ``src/test/kotlin/com/template/FlowTests.kt`` * ``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 Progress so far
--------------- ---------------
We now have a template that we can build upon to define our IOU CorDapp. We now have a template that we can build upon to define our IOU CorDapp. Let's start by defining the ``IOUState``.
We'll begin writing the CorDapp proper by writing the definition of the ``IOUState``.

View File

@ -14,7 +14,7 @@ Want to see Corda running? Download our demonstration application `DemoBench <ht
follow our :doc:`quickstart guide </quickstart-index>`. 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 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`_. `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 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