Merge remote-tracking branch 'corda-public/master'
@ -35,10 +35,6 @@ buildscript {
|
|||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
// TODO: Remove this once all packages are published to jcenter or maven central. (M6 or 7).
|
|
||||||
maven {
|
|
||||||
url "http://r3.bintray.com/corda"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
@ -145,7 +141,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
|
|||||||
nearestCity "London"
|
nearestCity "London"
|
||||||
advertisedServices = ["corda.notary.validating"]
|
advertisedServices = ["corda.notary.validating"]
|
||||||
artemisPort 10002
|
artemisPort 10002
|
||||||
webPort 10003
|
|
||||||
cordapps = []
|
cordapps = []
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
|
@ -124,7 +124,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
|
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
recipient = aliceNode.legalIdentity,
|
recipient = aliceNode.legalIdentity,
|
||||||
notary = notaryNode.notaryIdentity
|
notary = notaryNode.notaryIdentity
|
||||||
))
|
)).returnValue.toBlocking().first()
|
||||||
|
|
||||||
rpc.startFlow(::CashFlow, CashCommand.PayCash(
|
rpc.startFlow(::CashFlow, CashCommand.PayCash(
|
||||||
amount = Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
|
amount = Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
|
||||||
|
@ -9,6 +9,7 @@ import net.corda.core.messaging.StateMachineUpdate
|
|||||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||||
import net.corda.core.node.services.StateMachineTransactionMapping
|
import net.corda.core.node.services.StateMachineTransactionMapping
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.node.services.config.SSLConfiguration
|
import net.corda.node.services.config.SSLConfiguration
|
||||||
import net.corda.node.services.messaging.CordaRPCClient
|
import net.corda.node.services.messaging.CordaRPCClient
|
||||||
@ -52,7 +53,9 @@ class NodeMonitorModel {
|
|||||||
* TODO provide an unsubscribe mechanism
|
* TODO provide an unsubscribe mechanism
|
||||||
*/
|
*/
|
||||||
fun register(nodeHostAndPort: HostAndPort, sslConfig: SSLConfiguration, username: String, password: String) {
|
fun register(nodeHostAndPort: HostAndPort, sslConfig: SSLConfiguration, username: String, password: String) {
|
||||||
val client = CordaRPCClient(nodeHostAndPort, sslConfig)
|
val client = CordaRPCClient(nodeHostAndPort, sslConfig){
|
||||||
|
maxRetryInterval = 10.seconds.toMillis()
|
||||||
|
}
|
||||||
client.start(username, password)
|
client.start(username, password)
|
||||||
val proxy = client.proxy()
|
val proxy = client.proxy()
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
|
|
||||||
class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() {
|
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException() {
|
||||||
override fun toString() = "Insufficient balance, missing $amountMissing"
|
override fun toString() = "Insufficient balance, missing $amountMissing"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,17 @@ import java.security.PublicKey
|
|||||||
* cluster of Corda nodes. A [Party] representing a distributed service will use a composite key containing all
|
* cluster of Corda nodes. A [Party] representing a distributed service will use a composite key containing all
|
||||||
* individual cluster nodes' public keys. Each of the nodes in the cluster will advertise the same group [Party].
|
* individual cluster nodes' public keys. Each of the nodes in the cluster will advertise the same group [Party].
|
||||||
*
|
*
|
||||||
|
* Note that equality is based solely on the owning key.
|
||||||
|
*
|
||||||
* @see CompositeKey
|
* @see CompositeKey
|
||||||
*/
|
*/
|
||||||
data class Party(val name: String, val owningKey: CompositeKey) {
|
class Party(val name: String, val owningKey: CompositeKey) {
|
||||||
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
|
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
|
||||||
constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite)
|
constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite)
|
||||||
|
|
||||||
|
/** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */
|
||||||
|
override fun equals(other: Any?): Boolean = other is Party && this.owningKey == other.owningKey
|
||||||
|
override fun hashCode(): Int = owningKey.hashCode()
|
||||||
override fun toString() = name
|
override fun toString() = name
|
||||||
|
|
||||||
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
|
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
|
||||||
|
13
core/src/main/kotlin/net/corda/core/flows/FlowException.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package net.corda.core.flows
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception which can be thrown by a [FlowLogic] at any point in its logic to unexpectedly bring it to a permanent end.
|
||||||
|
* The exception will propagate to all counterparty flows and will be thrown on their end the next time they wait on a
|
||||||
|
* [FlowLogic.receive] or [FlowLogic.sendAndReceive]. Any flow which no longer needs to do a receive, or has already ended,
|
||||||
|
* will not receive the exception (if this is required then have them wait for a confirmation message).
|
||||||
|
*
|
||||||
|
* [FlowException] (or a subclass) can be a valid expected response from a flow, particularly ones which act as a service.
|
||||||
|
* It is recommended a [FlowLogic] document the [FlowException] types it can throw.
|
||||||
|
*/
|
||||||
|
open class FlowException @JvmOverloads constructor(message: String? = null, cause: Throwable? = null)
|
||||||
|
: Exception(message, cause)
|
@ -73,7 +73,7 @@ abstract class FlowLogic<out T> {
|
|||||||
* @returns an [UntrustworthyData] wrapper around the received object.
|
* @returns an [UntrustworthyData] wrapper around the received object.
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
@Suspendable
|
||||||
open fun <T : Any> sendAndReceive(receiveType: Class<T>, otherParty: Party, payload: Any): UntrustworthyData<T> {
|
open fun <R : Any> sendAndReceive(receiveType: Class<R>, otherParty: Party, payload: Any): UntrustworthyData<R> {
|
||||||
return stateMachine.sendAndReceive(receiveType, otherParty, payload, sessionFlow)
|
return stateMachine.sendAndReceive(receiveType, otherParty, payload, sessionFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +92,11 @@ abstract class FlowLogic<out T> {
|
|||||||
* Remember that when receiving data from other parties the data should not be trusted until it's been thoroughly
|
* Remember that when receiving data from other parties the data should not be trusted until it's been thoroughly
|
||||||
* verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly
|
* verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly
|
||||||
* corrupted data in order to exploit your code.
|
* corrupted data in order to exploit your code.
|
||||||
|
*
|
||||||
|
* @returns an [UntrustworthyData] wrapper around the received object.
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
@Suspendable
|
||||||
open fun <T : Any> receive(receiveType: Class<T>, otherParty: Party): UntrustworthyData<T> {
|
open fun <R : Any> receive(receiveType: Class<R>, otherParty: Party): UntrustworthyData<R> {
|
||||||
return stateMachine.receive(receiveType, otherParty, sessionFlow)
|
return stateMachine.receive(receiveType, otherParty, sessionFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +118,15 @@ abstract class FlowLogic<out T> {
|
|||||||
* @param shareParentSessions In certain situations the need arises to use the same sessions the parent flow has
|
* @param shareParentSessions In certain situations the need arises to use the same sessions the parent flow has
|
||||||
* already established. However this also prevents the subflow from creating new sessions with those parties.
|
* already established. However this also prevents the subflow from creating new sessions with those parties.
|
||||||
* For this reason the default value is false.
|
* For this reason the default value is false.
|
||||||
|
*
|
||||||
|
* @throws FlowException This is either thrown by [subLogic] itself or propagated from any of the remote
|
||||||
|
* [FlowLogic]s it communicated with. A subflow retry can be done by catching this exception.
|
||||||
*/
|
*/
|
||||||
// TODO Rethink the default value for shareParentSessions
|
// TODO Rethink the default value for shareParentSessions
|
||||||
// TODO shareParentSessions is a bit too low-level and perhaps can be expresed in a better way
|
// TODO shareParentSessions is a bit too low-level and perhaps can be expresed in a better way
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
|
@Throws(FlowException::class)
|
||||||
open fun <R> subFlow(subLogic: FlowLogic<R>, shareParentSessions: Boolean = false): R {
|
open fun <R> subFlow(subLogic: FlowLogic<R>, shareParentSessions: Boolean = false): R {
|
||||||
subLogic.stateMachine = stateMachine
|
subLogic.stateMachine = stateMachine
|
||||||
maybeWireUpProgressTracking(subLogic)
|
maybeWireUpProgressTracking(subLogic)
|
||||||
@ -149,6 +155,7 @@ abstract class FlowLogic<out T> {
|
|||||||
* helpful if this flow is meant to be used as a subflow.
|
* helpful if this flow is meant to be used as a subflow.
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(FlowException::class)
|
||||||
abstract fun call(): T
|
abstract fun call(): T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +188,6 @@ abstract class FlowLogic<out T> {
|
|||||||
|
|
||||||
private fun maybeWireUpProgressTracking(subLogic: FlowLogic<*>) {
|
private fun maybeWireUpProgressTracking(subLogic: FlowLogic<*>) {
|
||||||
val ours = progressTracker
|
val ours = progressTracker
|
||||||
|
|
||||||
val theirs = subLogic.progressTracker
|
val theirs = subLogic.progressTracker
|
||||||
if (ours != null && theirs != null) {
|
if (ours != null && theirs != null) {
|
||||||
if (ours.currentStep == ProgressTracker.UNSTARTED) {
|
if (ours.currentStep == ProgressTracker.UNSTARTED) {
|
||||||
|
@ -40,5 +40,3 @@ interface FlowStateMachine<R> {
|
|||||||
val id: StateMachineRunId
|
val id: StateMachineRunId
|
||||||
val resultFuture: ListenableFuture<R>
|
val resultFuture: ListenableFuture<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlowException(message: String) : RuntimeException(message)
|
|
||||||
|
@ -102,11 +102,21 @@ interface CordaRPCOps : RPCOps {
|
|||||||
*/
|
*/
|
||||||
fun uploadAttachment(jar: InputStream): SecureHash
|
fun uploadAttachment(jar: InputStream): SecureHash
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
@Deprecated("This service will be removed in a future milestone")
|
||||||
|
fun uploadFile(dataType: String, name: String?, file: InputStream): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the node-local current time.
|
* Returns the node-local current time.
|
||||||
*/
|
*/
|
||||||
fun currentNodeTime(): Instant
|
fun currentNodeTime(): Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Observable emitting a single Unit once the node is registered with the network map.
|
||||||
|
*/
|
||||||
|
@RPCReturnsObservables
|
||||||
|
fun waitUntilRegisteredWithNetworkMap(): Observable<Unit>
|
||||||
|
|
||||||
// TODO These need rethinking. Instead of these direct calls we should have a way of replicating a subset of
|
// TODO These need rethinking. Instead of these direct calls we should have a way of replicating a subset of
|
||||||
// the node's state locally and query that directly.
|
// the node's state locally and query that directly.
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,10 @@ data class NodeInfo(val address: SingleMessageRecipient,
|
|||||||
val legalIdentity: Party,
|
val legalIdentity: Party,
|
||||||
var advertisedServices: List<ServiceEntry> = emptyList(),
|
var advertisedServices: List<ServiceEntry> = emptyList(),
|
||||||
val physicalLocation: PhysicalLocation? = null) {
|
val physicalLocation: PhysicalLocation? = null) {
|
||||||
|
init {
|
||||||
|
require(advertisedServices.none { it.identity == legalIdentity }) { "Service identities must be different from node legal identity" }
|
||||||
|
}
|
||||||
|
|
||||||
val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity
|
val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity
|
||||||
fun serviceIdentities(type: ServiceType): List<Party> = advertisedServices.filter { it.info.type.isSubTypeOf(type) }.map { it.identity }
|
fun serviceIdentities(type: ServiceType): List<Party> = advertisedServices.filter { it.info.type.isSubTypeOf(type) }.map { it.identity }
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import net.corda.core.toFuture
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -216,6 +218,24 @@ interface KeyManagementService {
|
|||||||
fun freshKey(): KeyPair
|
fun freshKey(): KeyPair
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move to a more appropriate location
|
||||||
|
/**
|
||||||
|
* An interface that denotes a service that can accept file uploads.
|
||||||
|
*/
|
||||||
|
interface FileUploader {
|
||||||
|
/**
|
||||||
|
* Accepts the data in the given input stream, and returns some sort of useful return message that will be sent
|
||||||
|
* back to the user in the response.
|
||||||
|
*/
|
||||||
|
fun upload(file: InputStream): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this service accepts this type of upload. For example if you are uploading interest rates this could
|
||||||
|
* be "my-service-interest-rates". Type here does not refer to file extentions or MIME types.
|
||||||
|
*/
|
||||||
|
fun accepts(type: String): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sketch of an interface to a simple key/value storage system. Intended for persistence of simple blobs like
|
* A sketch of an interface to a simple key/value storage system. Intended for persistence of simple blobs like
|
||||||
* transactions, serialised flow state machines and so on. Again, this isn't intended to imply lack of SQL or
|
* transactions, serialised flow state machines and so on. Again, this isn't intended to imply lack of SQL or
|
||||||
@ -232,6 +252,10 @@ interface StorageService {
|
|||||||
/** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */
|
/** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */
|
||||||
val attachments: AttachmentStorage
|
val attachments: AttachmentStorage
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
@Deprecated("This service will be removed in a future milestone")
|
||||||
|
val uploaders: List<FileUploader>
|
||||||
|
|
||||||
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
|
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import com.esotericsoftware.kryo.io.Output
|
|||||||
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
||||||
import com.esotericsoftware.kryo.serializers.MapSerializer
|
import com.esotericsoftware.kryo.serializers.MapSerializer
|
||||||
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
||||||
|
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
|
||||||
import de.javakaffee.kryoserializers.guava.*
|
import de.javakaffee.kryoserializers.guava.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
@ -402,12 +403,10 @@ fun createKryo(k: Kryo = Kryo()): Kryo {
|
|||||||
// serialise the Kryo object itself when suspending a fiber. That's dumb, useless AND can cause crashes, so
|
// serialise the Kryo object itself when suspending a fiber. That's dumb, useless AND can cause crashes, so
|
||||||
// we avoid it here.
|
// we avoid it here.
|
||||||
register(Kryo::class.java, object : Serializer<Kryo>() {
|
register(Kryo::class.java, object : Serializer<Kryo>() {
|
||||||
override fun write(kryo: Kryo, output: Output, obj: Kryo) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<Kryo>): Kryo {
|
override fun read(kryo: Kryo, input: Input, type: Class<Kryo>): Kryo {
|
||||||
return createKryo((Fiber.getFiberSerializer() as KryoSerializer).kryo)
|
return createKryo((Fiber.getFiberSerializer() as KryoSerializer).kryo)
|
||||||
}
|
}
|
||||||
|
override fun write(kryo: Kryo, output: Output, obj: Kryo) {}
|
||||||
})
|
})
|
||||||
|
|
||||||
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
||||||
@ -441,6 +440,7 @@ fun createKryo(k: Kryo = Kryo()): Kryo {
|
|||||||
|
|
||||||
addDefaultSerializer(BufferedInputStream::class.java, InputStreamSerializer)
|
addDefaultSerializer(BufferedInputStream::class.java, InputStreamSerializer)
|
||||||
|
|
||||||
|
UnmodifiableCollectionsSerializer.registerSerializers(k)
|
||||||
ImmutableListSerializer.registerSerializers(k)
|
ImmutableListSerializer.registerSerializers(k)
|
||||||
ImmutableSetSerializer.registerSerializers(k)
|
ImmutableSetSerializer.registerSerializers(k)
|
||||||
ImmutableSortedSetSerializer.registerSerializers(k)
|
ImmutableSortedSetSerializer.registerSerializers(k)
|
||||||
|
@ -5,9 +5,11 @@ import net.corda.core.contracts.TransactionResolutionException
|
|||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.signWithECDSA
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
import java.security.KeyPair
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -132,4 +134,13 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
|
|||||||
*/
|
*/
|
||||||
@Throws(FileNotFoundException::class, TransactionResolutionException::class, SignaturesMissingException::class)
|
@Throws(FileNotFoundException::class, TransactionResolutionException::class, SignaturesMissingException::class)
|
||||||
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)
|
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to simplify the act of signing the transaction.
|
||||||
|
*
|
||||||
|
* @param keyPair the signer's public/private key pair.
|
||||||
|
*
|
||||||
|
* @return a digital signature of the transaction.
|
||||||
|
*/
|
||||||
|
fun signWithECDSA(keyPair: KeyPair) = keyPair.signWithECDSA(this.id.bytes)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.corda.core.utilities
|
package net.corda.core.utilities
|
||||||
|
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A small utility to approximate taint tracking: if a method gives you back one of these, it means the data came from
|
* A small utility to approximate taint tracking: if a method gives you back one of these, it means the data came from
|
||||||
* a remote source that may be incentivised to pass us junk that violates basic assumptions and thus must be checked
|
* a remote source that may be incentivised to pass us junk that violates basic assumptions and thus must be checked
|
||||||
@ -17,6 +19,7 @@ class UntrustworthyData<out T>(private val fromUntrustedWorld: T) {
|
|||||||
get() = fromUntrustedWorld
|
get() = fromUntrustedWorld
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@Throws(FlowException::class)
|
||||||
inline fun <R> unwrap(validator: (T) -> R) = validator(data)
|
inline fun <R> unwrap(validator: (T) -> R) = validator(data)
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.crypto.CompositeKey
|
|||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.signWithECDSA
|
import net.corda.core.crypto.signWithECDSA
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -27,26 +28,22 @@ import net.corda.flows.AbstractStateReplacementFlow.Instigator
|
|||||||
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
|
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
|
||||||
* use the new updated state for future transactions.
|
* use the new updated state for future transactions.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractStateReplacementFlow<T> {
|
abstract class AbstractStateReplacementFlow {
|
||||||
interface Proposal<out T> {
|
data class Proposal<out T>(val stateRef: StateRef, val modification: T, val stx: SignedTransaction)
|
||||||
val stateRef: StateRef
|
|
||||||
val modification: T
|
|
||||||
val stx: SignedTransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Instigator<out S : ContractState, T>(val originalState: StateAndRef<S>,
|
abstract class Instigator<out S : ContractState, out T>(
|
||||||
|
val originalState: StateAndRef<S>,
|
||||||
val modification: T,
|
val modification: T,
|
||||||
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<StateAndRef<S>>() {
|
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<StateAndRef<S>>() {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
|
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
|
||||||
|
|
||||||
object NOTARY : ProgressTracker.Step("Requesting notary signature")
|
object NOTARY : ProgressTracker.Step("Requesting notary signature")
|
||||||
|
|
||||||
fun tracker() = ProgressTracker(SIGNING, NOTARY)
|
fun tracker() = ProgressTracker(SIGNING, NOTARY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(StateReplacementException::class)
|
||||||
override fun call(): StateAndRef<S> {
|
override fun call(): StateAndRef<S> {
|
||||||
val (stx, participants) = assembleTx()
|
val (stx, participants) = assembleTx()
|
||||||
|
|
||||||
@ -66,7 +63,6 @@ abstract class AbstractStateReplacementFlow<T> {
|
|||||||
return finalTx.tx.outRef(0)
|
return finalTx.tx.outRef(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
|
|
||||||
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>>
|
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>>
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@ -89,59 +85,55 @@ abstract class AbstractStateReplacementFlow<T> {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getParticipantSignature(party: Party, stx: SignedTransaction): DigitalSignature.WithKey {
|
private fun getParticipantSignature(party: Party, stx: SignedTransaction): DigitalSignature.WithKey {
|
||||||
val proposal = assembleProposal(originalState.ref, modification, stx)
|
val proposal = Proposal(originalState.ref, modification, stx)
|
||||||
|
val response = sendAndReceive<DigitalSignature.WithKey>(party, proposal)
|
||||||
val response = sendAndReceive<Result>(party, proposal)
|
return response.unwrap {
|
||||||
val participantSignature = response.unwrap {
|
check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" }
|
||||||
if (it.sig == null) throw StateReplacementException(it.error!!)
|
it.verifyWithECDSA(stx.id)
|
||||||
else {
|
it
|
||||||
check(party.owningKey.isFulfilledBy(it.sig.by)) { "Not signed by the required participant" }
|
|
||||||
it.sig.verifyWithECDSA(stx.id)
|
|
||||||
it.sig
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return participantSignature
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
|
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.WithKey {
|
||||||
progressTracker.currentStep = NOTARY
|
progressTracker.currentStep = NOTARY
|
||||||
|
try {
|
||||||
return subFlow(NotaryFlow.Client(stx))
|
return subFlow(NotaryFlow.Client(stx))
|
||||||
|
} catch (e: NotaryException) {
|
||||||
|
throw StateReplacementException("Unable to notarise state change", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Acceptor<T>(val otherSide: Party,
|
abstract class Acceptor<in T>(val otherSide: Party,
|
||||||
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
|
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")
|
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")
|
||||||
|
|
||||||
object APPROVING : ProgressTracker.Step("State replacement approved")
|
object APPROVING : ProgressTracker.Step("State replacement approved")
|
||||||
|
|
||||||
object REJECTING : ProgressTracker.Step("State replacement rejected")
|
fun tracker() = ProgressTracker(VERIFYING, APPROVING)
|
||||||
|
|
||||||
fun tracker() = ProgressTracker(VERIFYING, APPROVING, REJECTING)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(StateReplacementException::class)
|
||||||
override fun call() {
|
override fun call() {
|
||||||
progressTracker.currentStep = VERIFYING
|
progressTracker.currentStep = VERIFYING
|
||||||
val maybeProposal: UntrustworthyData<Proposal<T>> = receive(otherSide)
|
val maybeProposal: UntrustworthyData<Proposal<T>> = receive(otherSide)
|
||||||
try {
|
val stx: SignedTransaction = maybeProposal.unwrap {
|
||||||
val stx: SignedTransaction = maybeProposal.unwrap { verifyProposal(maybeProposal).stx }
|
verifyProposal(it)
|
||||||
verifyTx(stx)
|
verifyTx(it.stx)
|
||||||
approve(stx)
|
it.stx
|
||||||
} catch(e: Exception) {
|
|
||||||
// TODO: catch only specific exceptions. However, there are numerous validation exceptions
|
|
||||||
// that might occur (tx validation/resolution, invalid proposal). Need to rethink how
|
|
||||||
// we manage exceptions and maybe introduce some platform exception hierarchy
|
|
||||||
val myIdentity = serviceHub.myInfo.legalIdentity
|
|
||||||
val state = maybeProposal.unwrap { it.stateRef }
|
|
||||||
val reason = StateReplacementRefused(myIdentity, state, e.message)
|
|
||||||
|
|
||||||
reject(reason)
|
|
||||||
}
|
}
|
||||||
|
approve(stx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
private fun verifyTx(stx: SignedTransaction) {
|
||||||
|
checkMySignatureRequired(stx.tx)
|
||||||
|
checkDependenciesValid(stx)
|
||||||
|
// We expect stx to have insufficient signatures, so we convert the WireTransaction to the LedgerTransaction
|
||||||
|
// here, thus bypassing the sufficient-signatures check.
|
||||||
|
stx.tx.toLedgerTransaction(serviceHub).verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
@ -149,8 +141,7 @@ abstract class AbstractStateReplacementFlow<T> {
|
|||||||
progressTracker.currentStep = APPROVING
|
progressTracker.currentStep = APPROVING
|
||||||
|
|
||||||
val mySignature = sign(stx)
|
val mySignature = sign(stx)
|
||||||
val response = Result.noError(mySignature)
|
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(otherSide, mySignature)
|
||||||
val swapSignatures = sendAndReceive<List<DigitalSignature.WithKey>>(otherSide, response)
|
|
||||||
|
|
||||||
// TODO: This step should not be necessary, as signatures are re-checked in verifySignatures.
|
// TODO: This step should not be necessary, as signatures are re-checked in verifySignatures.
|
||||||
val allSignatures = swapSignatures.unwrap { signatures ->
|
val allSignatures = swapSignatures.unwrap { signatures ->
|
||||||
@ -163,28 +154,13 @@ abstract class AbstractStateReplacementFlow<T> {
|
|||||||
serviceHub.recordTransactions(finalTx)
|
serviceHub.recordTransactions(finalTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
private fun reject(e: StateReplacementRefused) {
|
|
||||||
progressTracker.currentStep = REJECTING
|
|
||||||
val response = Result.withError(e)
|
|
||||||
send(otherSide, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the state change proposal to confirm that it's acceptable to this node. Rules for verification depend
|
* Check the state change proposal to confirm that it's acceptable to this node. Rules for verification depend
|
||||||
* on the change proposed, and may further depend on the node itself (for example configuration). The
|
* on the change proposed, and may further depend on the node itself (for example configuration). The
|
||||||
* proposal is returned if acceptable, otherwise an exception is thrown.
|
* proposal is returned if acceptable, otherwise a [StateReplacementException] is thrown.
|
||||||
*/
|
*/
|
||||||
abstract protected fun verifyProposal(maybeProposal: UntrustworthyData<Proposal<T>>): Proposal<T>
|
@Throws(StateReplacementException::class)
|
||||||
|
abstract protected fun verifyProposal(proposal: Proposal<T>)
|
||||||
@Suspendable
|
|
||||||
private fun verifyTx(stx: SignedTransaction) {
|
|
||||||
checkMySignatureRequired(stx.tx)
|
|
||||||
checkDependenciesValid(stx)
|
|
||||||
// We expect stx to have insufficient signatures, so we convert the WireTransaction to the LedgerTransaction
|
|
||||||
// here, thus bypassing the sufficient-signatures check.
|
|
||||||
stx.tx.toLedgerTransaction(serviceHub).verify()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkMySignatureRequired(tx: WireTransaction) {
|
private fun checkMySignatureRequired(tx: WireTransaction) {
|
||||||
// TODO: use keys from the keyManagementService instead
|
// TODO: use keys from the keyManagementService instead
|
||||||
@ -202,20 +178,7 @@ abstract class AbstractStateReplacementFlow<T> {
|
|||||||
return myKey.signWithECDSA(stx.id)
|
return myKey.signWithECDSA(stx.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: similar classes occur in other places (NotaryFlow), need to consolidate
|
|
||||||
data class Result private constructor(val sig: DigitalSignature.WithKey?, val error: StateReplacementRefused?) {
|
|
||||||
companion object {
|
|
||||||
fun withError(error: StateReplacementRefused) = Result(null, error)
|
|
||||||
fun noError(sig: DigitalSignature.WithKey) = Result(sig, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class StateReplacementException @JvmOverloads constructor(message: String? = null, cause: Throwable? = null)
|
||||||
/** Thrown when a participant refuses the proposed state replacement */
|
: FlowException(message, cause)
|
||||||
class StateReplacementRefused(val identity: Party, val state: StateRef, val detail: String?) {
|
|
||||||
override fun toString() = "A participant $identity refused to change state $state: " + (detail ?: "no reason provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateReplacementException(val error: StateReplacementRefused) : Exception("State change failed - $error")
|
|
||||||
|
@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.contracts.NamedByHash
|
import net.corda.core.contracts.NamedByHash
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch
|
import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch
|
||||||
@ -30,14 +31,15 @@ abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
|
|||||||
protected val requests: Set<SecureHash>,
|
protected val requests: Set<SecureHash>,
|
||||||
protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
|
protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
|
||||||
|
|
||||||
open class BadAnswer : Exception()
|
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException()
|
||||||
class HashNotFound(val requested: SecureHash) : BadAnswer()
|
class DownloadedVsRequestedSizeMismatch(val requested: Int, val got: Int) : IllegalArgumentException()
|
||||||
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : BadAnswer()
|
class HashNotFound(val requested: SecureHash) : FlowException()
|
||||||
|
|
||||||
data class Request(val hashes: List<SecureHash>)
|
data class Request(val hashes: List<SecureHash>)
|
||||||
data class Result<out T : NamedByHash>(val fromDisk: List<T>, val downloaded: List<T>)
|
data class Result<out T : NamedByHash>(val fromDisk: List<T>, val downloaded: List<T>)
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(HashNotFound::class)
|
||||||
override fun call(): Result<T> {
|
override fun call(): Result<T> {
|
||||||
// Load the items we have from disk and figure out which we're missing.
|
// Load the items we have from disk and figure out which we're missing.
|
||||||
val (fromDisk, toFetch) = loadWhatWeHave()
|
val (fromDisk, toFetch) = loadWhatWeHave()
|
||||||
@ -48,7 +50,7 @@ abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
|
|||||||
logger.trace("Requesting ${toFetch.size} dependency(s) for verification")
|
logger.trace("Requesting ${toFetch.size} dependency(s) for verification")
|
||||||
|
|
||||||
// TODO: Support "large message" response streaming so response sizes are not limited by RAM.
|
// TODO: Support "large message" response streaming so response sizes are not limited by RAM.
|
||||||
val maybeItems = sendAndReceive<ArrayList<W?>>(otherSide, Request(toFetch))
|
val maybeItems = sendAndReceive<ArrayList<W>>(otherSide, Request(toFetch))
|
||||||
// Check for a buggy/malicious peer answering with something that we didn't ask for.
|
// Check for a buggy/malicious peer answering with something that we didn't ask for.
|
||||||
val downloaded = validateFetchResponse(maybeItems, toFetch)
|
val downloaded = validateFetchResponse(maybeItems, toFetch)
|
||||||
maybeWriteToDisk(downloaded)
|
maybeWriteToDisk(downloaded)
|
||||||
@ -78,22 +80,19 @@ abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
protected open fun convert(wire: W): T = wire as T
|
protected open fun convert(wire: W): T = wire as T
|
||||||
|
|
||||||
private fun validateFetchResponse(maybeItems: UntrustworthyData<ArrayList<W?>>,
|
private fun validateFetchResponse(maybeItems: UntrustworthyData<ArrayList<W>>,
|
||||||
requests: List<SecureHash>): List<T> =
|
requests: List<SecureHash>): List<T> {
|
||||||
maybeItems.unwrap { response ->
|
return maybeItems.unwrap { response ->
|
||||||
if (response.size != requests.size)
|
if (response.size != requests.size)
|
||||||
throw BadAnswer()
|
throw DownloadedVsRequestedSizeMismatch(requests.size, response.size)
|
||||||
for ((index, resp) in response.withIndex()) {
|
val answers = response.map { convert(it) }
|
||||||
if (resp == null) throw HashNotFound(requests[index])
|
|
||||||
}
|
|
||||||
val answers = response.requireNoNulls().map { convert(it) }
|
|
||||||
// Check transactions actually hash to what we requested, if this fails the remote node
|
// Check transactions actually hash to what we requested, if this fails the remote node
|
||||||
// is a malicious flow violator or buggy.
|
// is a malicious flow violator or buggy.
|
||||||
for ((index, item) in answers.withIndex())
|
for ((index, item) in answers.withIndex()) {
|
||||||
if (item.id != requests[index])
|
if (item.id != requests[index])
|
||||||
throw DownloadedVsRequestedDataMismatch(requests[index], item.id)
|
throw DownloadedVsRequestedDataMismatch(requests[index], item.id)
|
||||||
|
}
|
||||||
answers
|
answers
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ class FinalityFlow(val transaction: SignedTransaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(NotaryException::class)
|
||||||
override fun call() {
|
override fun call() {
|
||||||
// TODO: Resolve the tx here: it's probably already been done, but re-resolution is a no-op and it'll make the API more forgiving.
|
// TODO: Resolve the tx here: it's probably already been done, but re-resolution is a no-op and it'll make the API more forgiving.
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package net.corda.flows
|
package net.corda.flows
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.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 net.corda.core.utilities.UntrustworthyData
|
|
||||||
import net.corda.flows.NotaryChangeFlow.Acceptor
|
import net.corda.flows.NotaryChangeFlow.Acceptor
|
||||||
import net.corda.flows.NotaryChangeFlow.Instigator
|
import net.corda.flows.NotaryChangeFlow.Instigator
|
||||||
|
|
||||||
@ -20,19 +18,12 @@ import net.corda.flows.NotaryChangeFlow.Instigator
|
|||||||
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
|
* Finally, [Instigator] sends the transaction containing all signatures back to each participant so they can record it and
|
||||||
* use the new updated state for future transactions.
|
* use the new updated state for future transactions.
|
||||||
*/
|
*/
|
||||||
object NotaryChangeFlow : AbstractStateReplacementFlow<Party>() {
|
object NotaryChangeFlow : AbstractStateReplacementFlow() {
|
||||||
|
|
||||||
data class Proposal(override val stateRef: StateRef,
|
class Instigator<out T : ContractState>(
|
||||||
override val modification: Party,
|
originalState: StateAndRef<T>,
|
||||||
override val stx: SignedTransaction) : AbstractStateReplacementFlow.Proposal<Party>
|
|
||||||
|
|
||||||
class Instigator<T : ContractState>(originalState: StateAndRef<T>,
|
|
||||||
newNotary: Party,
|
newNotary: Party,
|
||||||
progressTracker: ProgressTracker = tracker())
|
progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Instigator<T, Party>(originalState, newNotary, progressTracker) {
|
||||||
: AbstractStateReplacementFlow.Instigator<T, Party>(originalState, newNotary, progressTracker) {
|
|
||||||
|
|
||||||
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementFlow.Proposal<Party>
|
|
||||||
= Proposal(stateRef, modification, stx)
|
|
||||||
|
|
||||||
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> {
|
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> {
|
||||||
val state = originalState.state
|
val state = originalState.state
|
||||||
@ -66,7 +57,8 @@ object NotaryChangeFlow : AbstractStateReplacementFlow<Party>() {
|
|||||||
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<CompositeKey> {
|
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<CompositeKey> {
|
||||||
val stateRef = originalState.ref
|
val stateRef = originalState.ref
|
||||||
val txId = stateRef.txhash
|
val txId = stateRef.txhash
|
||||||
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId) ?: throw IllegalStateException("Transaction $txId not found")
|
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
|
||||||
|
?: throw StateReplacementException("Transaction $txId not found")
|
||||||
val outputs = issuingTx.tx.outputs
|
val outputs = issuingTx.tx.outputs
|
||||||
|
|
||||||
val participants = mutableSetOf<CompositeKey>()
|
val participants = mutableSetOf<CompositeKey>()
|
||||||
@ -97,8 +89,7 @@ object NotaryChangeFlow : AbstractStateReplacementFlow<Party>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Acceptor(otherSide: Party,
|
class Acceptor(otherSide: Party,
|
||||||
override val progressTracker: ProgressTracker = tracker())
|
override val progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Acceptor<Party>(otherSide) {
|
||||||
: AbstractStateReplacementFlow.Acceptor<Party>(otherSide) {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the notary change proposal.
|
* Check the notary change proposal.
|
||||||
@ -107,26 +98,28 @@ object NotaryChangeFlow : AbstractStateReplacementFlow<Party>() {
|
|||||||
* and is also in a geographically convenient location we can just automatically approve the change.
|
* and is also in a geographically convenient location we can just automatically approve the change.
|
||||||
* TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal
|
* TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal
|
||||||
*/
|
*/
|
||||||
@Suspendable
|
override fun verifyProposal(proposal: AbstractStateReplacementFlow.Proposal<Party>): Unit {
|
||||||
override fun verifyProposal(maybeProposal: UntrustworthyData<AbstractStateReplacementFlow.Proposal<Party>>): AbstractStateReplacementFlow.Proposal<Party> {
|
|
||||||
return maybeProposal.unwrap { proposal ->
|
|
||||||
val newNotary = proposal.modification
|
|
||||||
val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.notaryIdentity == newNotary }
|
|
||||||
require(isNotary) { "The proposed node $newNotary does not run a Notary service " }
|
|
||||||
|
|
||||||
val state = proposal.stateRef
|
val state = proposal.stateRef
|
||||||
val proposedTx = proposal.stx.tx
|
val proposedTx = proposal.stx.tx
|
||||||
require(state in proposedTx.inputs) { "The proposed state $state is not in the proposed transaction inputs" }
|
|
||||||
require(proposedTx.type.javaClass == TransactionType.NotaryChange::class.java) {
|
if (proposedTx.type !is TransactionType.NotaryChange) {
|
||||||
"The proposed transaction is not a notary change transaction."
|
throw StateReplacementException("The proposed transaction is not a notary change transaction.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// An example requirement
|
val newNotary = proposal.modification
|
||||||
val blacklist = listOf("Evil Notary")
|
val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.notaryIdentity == newNotary }
|
||||||
require(!blacklist.contains(newNotary.name)) { "The proposed new notary $newNotary is not trusted by the party" }
|
if (!isNotary) {
|
||||||
|
throw StateReplacementException("The proposed node $newNotary does not run a Notary service")
|
||||||
proposal
|
|
||||||
}
|
}
|
||||||
|
if (state !in proposedTx.inputs) {
|
||||||
|
throw StateReplacementException("The proposed state $state is not in the proposed transaction inputs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// // An example requirement
|
||||||
|
// val blacklist = listOf("Evil Notary")
|
||||||
|
// checkProposal(newNotary.name !in blacklist) {
|
||||||
|
// "The proposed new notary $newNotary is not trusted by the party"
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.Party
|
|||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.crypto.signWithECDSA
|
import net.corda.core.crypto.signWithECDSA
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.node.services.TimestampChecker
|
import net.corda.core.node.services.TimestampChecker
|
||||||
import net.corda.core.node.services.UniquenessException
|
import net.corda.core.node.services.UniquenessException
|
||||||
import net.corda.core.node.services.UniquenessProvider
|
import net.corda.core.node.services.UniquenessProvider
|
||||||
@ -13,7 +14,6 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
|
||||||
|
|
||||||
object NotaryFlow {
|
object NotaryFlow {
|
||||||
|
|
||||||
@ -29,9 +29,7 @@ object NotaryFlow {
|
|||||||
constructor(stx: SignedTransaction) : this(stx, Client.tracker())
|
constructor(stx: SignedTransaction) : this(stx, Client.tracker())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
object REQUESTING : ProgressTracker.Step("Requesting signature by Notary service")
|
object REQUESTING : ProgressTracker.Step("Requesting signature by Notary service")
|
||||||
|
|
||||||
object VALIDATING : ProgressTracker.Step("Validating response from Notary service")
|
object VALIDATING : ProgressTracker.Step("Validating response from Notary service")
|
||||||
|
|
||||||
fun tracker() = ProgressTracker(REQUESTING, VALIDATING)
|
fun tracker() = ProgressTracker(REQUESTING, VALIDATING)
|
||||||
@ -40,6 +38,7 @@ object NotaryFlow {
|
|||||||
lateinit var notaryParty: Party
|
lateinit var notaryParty: Party
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(NotaryException::class)
|
||||||
override fun call(): DigitalSignature.WithKey {
|
override fun call(): DigitalSignature.WithKey {
|
||||||
progressTracker.currentStep = REQUESTING
|
progressTracker.currentStep = REQUESTING
|
||||||
val wtx = stx.tx
|
val wtx = stx.tx
|
||||||
@ -53,27 +52,18 @@ object NotaryFlow {
|
|||||||
throw NotaryException(NotaryError.SignaturesMissing(ex))
|
throw NotaryException(NotaryError.SignaturesMissing(ex))
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = sendAndReceive<Result>(notaryParty, SignRequest(stx))
|
val response = try {
|
||||||
|
sendAndReceive<DigitalSignature.WithKey>(notaryParty, SignRequest(stx))
|
||||||
return validateResponse(response)
|
} catch (e: NotaryException) {
|
||||||
|
if (e.error is NotaryError.Conflict) {
|
||||||
|
e.error.conflict.verified()
|
||||||
|
}
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(NotaryException::class, IllegalStateException::class)
|
return response.unwrap { sig ->
|
||||||
private fun validateResponse(response: UntrustworthyData<Result>): DigitalSignature.WithKey {
|
validateSignature(sig, stx.id.bytes)
|
||||||
return response.unwrap { notaryResult ->
|
sig
|
||||||
progressTracker.currentStep = VALIDATING
|
|
||||||
when (notaryResult) {
|
|
||||||
is Result.Success -> {
|
|
||||||
validateSignature(notaryResult.sig, stx.id.bytes)
|
|
||||||
notaryResult.sig
|
|
||||||
}
|
|
||||||
is Result.Error -> {
|
|
||||||
if (notaryResult.error is NotaryError.Conflict)
|
|
||||||
notaryResult.error.conflict.verified()
|
|
||||||
throw NotaryException(notaryResult.error)
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException("Received invalid result from Notary service '$notaryParty'")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,17 +91,11 @@ object NotaryFlow {
|
|||||||
val stx = receive<SignRequest>(otherSide).unwrap { it.tx }
|
val stx = receive<SignRequest>(otherSide).unwrap { it.tx }
|
||||||
val wtx = stx.tx
|
val wtx = stx.tx
|
||||||
|
|
||||||
val result = try {
|
|
||||||
validateTimestamp(wtx)
|
validateTimestamp(wtx)
|
||||||
beforeCommit(stx)
|
beforeCommit(stx)
|
||||||
commitInputStates(wtx)
|
commitInputStates(wtx)
|
||||||
val sig = sign(stx.id.bytes)
|
val sig = sign(stx.id.bytes)
|
||||||
Result.Success(sig)
|
send(otherSide, sig)
|
||||||
} catch(e: NotaryException) {
|
|
||||||
Result.Error(e.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
send(otherSide, result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateTimestamp(tx: WireTransaction) {
|
private fun validateTimestamp(tx: WireTransaction) {
|
||||||
@ -164,15 +148,9 @@ object NotaryFlow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class SignRequest(val tx: SignedTransaction)
|
data class SignRequest(val tx: SignedTransaction)
|
||||||
|
|
||||||
sealed class Result {
|
|
||||||
class Error(val error: NotaryError) : Result()
|
|
||||||
class Success(val sig: DigitalSignature.WithKey) : Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotaryException(val error: NotaryError) : Exception() {
|
class NotaryException(val error: NotaryError) : FlowException() {
|
||||||
override fun toString() = "${super.toString()}: Error response from Notary - $error"
|
override fun toString() = "${super.toString()}: Error response from Notary - $error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,16 +50,12 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
|
|||||||
fun visit(transaction: SignedTransaction) {
|
fun visit(transaction: SignedTransaction) {
|
||||||
if (transaction.id !in visited) {
|
if (transaction.id !in visited) {
|
||||||
visited.add(transaction.id)
|
visited.add(transaction.id)
|
||||||
forwardGraph[transaction.id]?.forEach {
|
forwardGraph[transaction.id]?.forEach(::visit)
|
||||||
visit(it)
|
|
||||||
}
|
|
||||||
result.add(transaction)
|
result.add(transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions.forEach {
|
transactions.forEach(::visit)
|
||||||
visit(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.reverse()
|
result.reverse()
|
||||||
require(result.size == transactions.size)
|
require(result.size == transactions.size)
|
||||||
@ -93,6 +89,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@Throws(FetchDataFlow.HashNotFound::class)
|
||||||
override fun call(): List<LedgerTransaction> {
|
override fun call(): List<LedgerTransaction> {
|
||||||
val newTxns: Iterable<SignedTransaction> = topologicalSort(downloadDependencies(txHashes))
|
val newTxns: Iterable<SignedTransaction> = topologicalSort(downloadDependencies(txHashes))
|
||||||
|
|
||||||
|
BIN
docs/build/doctrees/CLI-vs-IDE.doctree
vendored
BIN
docs/build/doctrees/azure-vm.doctree
vendored
BIN
docs/build/doctrees/building-the-docs.doctree
vendored
BIN
docs/build/doctrees/clauses.doctree
vendored
BIN
docs/build/doctrees/clientrpc.doctree
vendored
BIN
docs/build/doctrees/codestyle.doctree
vendored
BIN
docs/build/doctrees/consensus.doctree
vendored
BIN
docs/build/doctrees/contract-catalogue.doctree
vendored
BIN
docs/build/doctrees/contract-irs.doctree
vendored
BIN
docs/build/doctrees/corda-configuration-file.doctree
vendored
BIN
docs/build/doctrees/corda-plugins.doctree
vendored
BIN
docs/build/doctrees/creating-a-cordapp.doctree
vendored
BIN
docs/build/doctrees/data-model.doctree
vendored
BIN
docs/build/doctrees/environment.pickle
vendored
BIN
docs/build/doctrees/event-scheduling.doctree
vendored
BIN
docs/build/doctrees/flow-state-machines.doctree
vendored
BIN
docs/build/doctrees/flow-testing.doctree
vendored
BIN
docs/build/doctrees/further-notes-on-kotlin.doctree
vendored
BIN
docs/build/doctrees/getting-set-up.doctree
vendored
BIN
docs/build/doctrees/glossary.doctree
vendored
BIN
docs/build/doctrees/index.doctree
vendored
BIN
docs/build/doctrees/inthebox.doctree
vendored
BIN
docs/build/doctrees/key-concepts-consensus-notaries.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-core-types.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-data-model.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-ecosystem.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-financial-model.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-flow-framework.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-security-model.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts-vault.doctree
vendored
Normal file
BIN
docs/build/doctrees/key-concepts.doctree
vendored
Normal file
BIN
docs/build/doctrees/loadtesting.doctree
vendored
BIN
docs/build/doctrees/merkle-trees.doctree
vendored
BIN
docs/build/doctrees/messaging.doctree
vendored
BIN
docs/build/doctrees/network-simulator.doctree
vendored
BIN
docs/build/doctrees/node-administration.doctree
vendored
BIN
docs/build/doctrees/node-explorer.doctree
vendored
BIN
docs/build/doctrees/node-services.doctree
vendored
BIN
docs/build/doctrees/oracles.doctree
vendored
BIN
docs/build/doctrees/permissioning.doctree
vendored
BIN
docs/build/doctrees/persistence.doctree
vendored
BIN
docs/build/doctrees/publishing-corda.doctree
vendored
BIN
docs/build/doctrees/release-notes.doctree
vendored
BIN
docs/build/doctrees/release-process.doctree
vendored
BIN
docs/build/doctrees/running-a-notary.doctree
vendored
BIN
docs/build/doctrees/running-the-demos.doctree
vendored
BIN
docs/build/doctrees/secure-coding-guidelines.doctree
vendored
BIN
docs/build/doctrees/transaction-data-types.doctree
vendored
BIN
docs/build/doctrees/tutorial-attachments.doctree
vendored
BIN
docs/build/doctrees/tutorial-clientrpc-api.doctree
vendored
BIN
docs/build/doctrees/tutorial-contract.doctree
vendored
BIN
docs/build/doctrees/tutorial-cordapp.doctree
vendored
BIN
docs/build/doctrees/tutorial-test-dsl.doctree
vendored
BIN
docs/build/doctrees/using-a-notary.doctree
vendored
2
docs/build/html/.buildinfo
vendored
@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: caa85c0cfb7660f75ff8985c07fa3b0c
|
config: 1768caf6e5e802b716b72241d5bd1c76
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
24
docs/build/html/CLI-vs-IDE.html
vendored
@ -34,8 +34,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="index" title="Index"
|
||||||
|
href="genindex.html"/>
|
||||||
|
<link rel="search" title="Search" href="search.html"/>
|
||||||
<link rel="top" title="R3 Corda latest documentation" href="index.html"/>
|
<link rel="top" title="R3 Corda latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Data model" href="data-model.html"/>
|
<link rel="next" title="Overview" href="key-concepts.html"/>
|
||||||
<link rel="prev" title="Running the demos" href="running-the-demos.html"/>
|
<link rel="prev" title="Running the demos" href="running-the-demos.html"/>
|
||||||
|
|
||||||
|
|
||||||
@ -116,11 +119,15 @@ API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/ja
|
|||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Key concepts</span></p>
|
<p class="caption"><span class="caption-text">Key concepts</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts.html">Overview</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="transaction-data-types.html">Data types</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-ecosystem.html">Corda ecosystem</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="merkle-trees.html">Transaction tear-offs</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-data-model.html">Data model</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="consensus.html">Consensus model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-core-types.html">Core types</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="clauses.html">Clauses</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-financial-model.html">Financial model</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-flow-framework.html">Flow framework</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-consensus-notaries.html">Consensus and notaries</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-vault.html">Vault</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="key-concepts-security-model.html">Security model</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">CorDapps</span></p>
|
<p class="caption"><span class="caption-text">CorDapps</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -158,6 +165,8 @@ API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/ja
|
|||||||
<p class="caption"><span class="caption-text">Other</span></p>
|
<p class="caption"><span class="caption-text">Other</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="network-simulator.html">Network Simulator</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="network-simulator.html">Network Simulator</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="clauses.html">Clauses</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="merkle-trees.html">Transaction tear-offs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Component library</span></p>
|
<p class="caption"><span class="caption-text">Component library</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -173,6 +182,7 @@ API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/ja
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="release-notes.html">Release notes</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="release-notes.html">Release notes</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="building-the-docs.html">Building the documentation</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="building-the-docs.html">Building the documentation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="further-notes-on-kotlin.html">Further notes on Kotlin</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="publishing-corda.html">Publishing Corda</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="publishing-corda.html">Publishing Corda</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="azure-vm.html">Working with the Corda Demo on Azure Marketplace</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="azure-vm.html">Working with the Corda Demo on Azure Marketplace</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -290,7 +300,7 @@ they are listed in the driver DSL configuration.</p>
|
|||||||
|
|
||||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||||
|
|
||||||
<a href="data-model.html" class="btn btn-neutral float-right" title="Data model" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
<a href="key-concepts.html" class="btn btn-neutral float-right" title="Overview" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||||
|
|
||||||
|
|
||||||
<a href="running-the-demos.html" class="btn btn-neutral" title="Running the demos" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
<a href="running-the-demos.html" class="btn btn-neutral" title="Running the demos" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||||
|
BIN
docs/build/html/_images/contract.png
vendored
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
docs/build/html/_images/cordaNetwork.png
vendored
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
docs/build/html/_images/financialContractStateModel.png
vendored
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
docs/build/html/_images/flowFramework.png
vendored
Normal file
After Width: | Height: | Size: 220 KiB |
BIN
docs/build/html/_images/vault.png
vendored
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 233 KiB |
BIN
docs/build/html/_images/vault1.png
vendored
Normal file
After Width: | Height: | Size: 371 KiB |
141
docs/build/html/_sources/consensus.txt
vendored
@ -1,141 +0,0 @@
|
|||||||
Consensus model
|
|
||||||
===============
|
|
||||||
|
|
||||||
The fundamental unit of consensus in Corda is the **state**. The concept of consensus can be divided into two parts:
|
|
||||||
|
|
||||||
1. Consensus over state **validity** -- parties can reach certainty that a transaction defining output states is accepted by the contracts pointed to by the states and has all the required signatures. This is achieved by parties independently running the same contract code and validation logic (as described in :doc:`data model <data-model>`)
|
|
||||||
|
|
||||||
2. Consensus over state **uniqueness** -- parties can reach certainty the output states created in a transaction are the unique successors to the input states consumed by that transaction (in other words -- a state has not been used as an input by more than one transaction)
|
|
||||||
|
|
||||||
This article presents an initial model for addressing the **uniqueness** problem.
|
|
||||||
|
|
||||||
.. note:: The current model is still a **work in progress** and everything described in this article can and is likely to change
|
|
||||||
|
|
||||||
Notary
|
|
||||||
------
|
|
||||||
|
|
||||||
We introduce the concept of a **notary**, which is an authority responsible for attesting that for a given transaction, it had not signed another transaction consuming any of its input states.
|
|
||||||
The data model is extended so that every **state** has an appointed notary:
|
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper for [ContractState] containing additional platform-level state information.
|
|
||||||
* This is the definitive state that is stored on the ledger and used in transaction outputs
|
|
||||||
*/
|
|
||||||
data class TransactionState<out T : ContractState>(
|
|
||||||
/** The custom contract state */
|
|
||||||
val data: T,
|
|
||||||
/** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
|
|
||||||
val notary: Party) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
All transactions have to be signed by their input state notary for the output states to be **valid** (apart from *issue* transactions, containing no input states).
|
|
||||||
|
|
||||||
.. note:: The notary is a logical concept and can itself be a distributed entity, potentially a cluster maintained by mutually distrusting parties
|
|
||||||
|
|
||||||
When the notary is requested to sign a transaction, it either signs over it, attesting that the outputs are the **unique** successors of the inputs,
|
|
||||||
or provides conflict information for any input state that had been consumed by another transaction it had signed before.
|
|
||||||
In doing so, the notary provides the point of finality in the system. Until the notary signature is obtained, parties cannot be sure that an equally valid, but conflicting transaction,
|
|
||||||
will not be regarded as confirmed. After the signature is obtained, the parties know that the inputs to this transaction have been uniquely consumed by this transaction.
|
|
||||||
Hence it is the point at which we can say finality has occurred.
|
|
||||||
|
|
||||||
Multiple notaries
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
More than one notary can exist in the network. This gives the following benefits:
|
|
||||||
|
|
||||||
* **Custom behaviour**. We can have both validating and privacy preserving notaries -- parties can make a choice based on their specific requirements
|
|
||||||
* **Load balancing**. Spreading the transaction load over multiple notaries will allow higher transaction throughput in the platform overall
|
|
||||||
* **Low latency**. Latency could be minimised by choosing a notary physically closer the transacting parties
|
|
||||||
|
|
||||||
A transaction should only be signed by a notary if all of its input states point to it.
|
|
||||||
In cases where a transaction involves states controlled by multiple notaries, the states first have to be repointed to the same notary.
|
|
||||||
This is achieved by using a special type of transaction that doesn't modify anything but the notary pointer of the state.
|
|
||||||
Ensuring that all input states point to the same notary is the responsibility of each involved party
|
|
||||||
(it is another condition for an output state of the transaction to be **valid**)
|
|
||||||
|
|
||||||
Changing notaries
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
To change the notary for an input state, use the ``NotaryChangeFlow``. For example:
|
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
fun changeNotary(originalState: StateAndRef<ContractState>,
|
|
||||||
newNotary: Party): StateAndRef<ContractState> {
|
|
||||||
val flow = NotaryChangeFlow.Instigator(originalState, newNotary)
|
|
||||||
return subFlow(flow)
|
|
||||||
}
|
|
||||||
|
|
||||||
The flow will:
|
|
||||||
|
|
||||||
1. Construct a transaction with the old state as the input and the new state as the output
|
|
||||||
|
|
||||||
2. Obtain signatures from all *participants* (a participant is any party that is able to consume this state in a valid transaction, as defined by the state itself)
|
|
||||||
|
|
||||||
3. Obtain the *old* notary signature
|
|
||||||
|
|
||||||
4. Record and distribute the final transaction to the participants so that everyone possesses the new state
|
|
||||||
|
|
||||||
.. note:: Eventually this will be handled automatically on demand.
|
|
||||||
|
|
||||||
Validation
|
|
||||||
----------
|
|
||||||
|
|
||||||
One of the design decisions for a notary is whether or not to **validate** a transaction before committing its input states.
|
|
||||||
|
|
||||||
If a transaction is not checked for validity, it opens the platform to "denial of state" attacks, where anyone can build an invalid transaction consuming someone else's states and submit it to the notary to get the states "blocked".
|
|
||||||
However, validation of a transaction requires the notary to be able to see the full contents of the transaction in question and its dependencies.
|
|
||||||
This is an obvious privacy leak.
|
|
||||||
|
|
||||||
Our platform is flexible and we currently support both validating and non-validating notary implementations -- a party can select which one to use based on its own privacy requirements.
|
|
||||||
|
|
||||||
.. note:: In the non-validating model the "denial of state" attack is partially alleviated by requiring the calling
|
|
||||||
party to authenticate and storing its identity for the request. The conflict information returned by the notary
|
|
||||||
specifies the consuming transaction ID along with the identity of the party that had requested the commit. If the
|
|
||||||
conflicting transaction is valid, the current one gets aborted; if not - a dispute can be raised and the input states
|
|
||||||
of the conflicting invalid transaction are "un-committed" (to be covered by legal process).
|
|
||||||
|
|
||||||
.. note:: At present all notaries can see the entire contents of a transaction, but we have a separate piece of work to
|
|
||||||
replace the parts of the transaction it does not require knowing about with hashes (only input references, timestamp
|
|
||||||
information, overall transaction ID and the necessary digests of the rest of the transaction to prove that the
|
|
||||||
referenced inputs/timestamps really do form part of the stated transaction ID should be visible).
|
|
||||||
|
|
||||||
Timestamping
|
|
||||||
------------
|
|
||||||
|
|
||||||
In this model the notary also acts as a *timestamping authority*, verifying the transaction timestamp command.
|
|
||||||
|
|
||||||
For a timestamp to be meaningful, its implications must be binding on the party requesting it.
|
|
||||||
A party can obtain a timestamp signature in order to prove that some event happened before/on/or after a particular point in time.
|
|
||||||
However, if the party is not also compelled to commit to the associated transaction, it has a choice of whether or not to reveal this fact until some point in the future.
|
|
||||||
As a result, we need to ensure that the notary either has to also sign the transaction within some time tolerance,
|
|
||||||
or perform timestamping *and* notarisation at the same time, which is the chosen behaviour for this model.
|
|
||||||
|
|
||||||
There will never be exact clock synchronisation between the party creating the transaction and the notary.
|
|
||||||
This is not only due to physics, network latencies, etc., but because between inserting the command and getting the
|
|
||||||
notary to sign there may be many other steps, like sending the transaction to other parties involved in the trade
|
|
||||||
as well, or even requesting human signoff. Thus the time observed by the notary may be quite different to the
|
|
||||||
time observed in step 1.
|
|
||||||
|
|
||||||
For this reason, times in transactions are specified as time *windows*, not absolute times. Time windows can be
|
|
||||||
open-ended, i.e. specify only one of "before" and "after" or they can be fully bounded. If a time window needs to
|
|
||||||
be converted to an absolute time for e.g. display purposes, there is a utility method on ``Timestamp`` to
|
|
||||||
calculate the mid point -- but in a distributed system there can never be "true time", only an approximation of it.
|
|
||||||
|
|
||||||
In this way we express that the *true value* of the fact "the current time" is actually unknowable. Even when both before and
|
|
||||||
after times are included, the transaction could have occurred at any point between those two timestamps. Here
|
|
||||||
"occurrence" could mean the execution date, the value date, the trade date etc ... the notary doesn't care what precise
|
|
||||||
meaning the timestamp has to the contract.
|
|
||||||
|
|
||||||
By creating a range that can be either closed or open at one end, we allow all of the following facts to be modelled:
|
|
||||||
|
|
||||||
* This transaction occurred at some point after the given time (e.g. after a maturity event)
|
|
||||||
* This transaction occurred at any time before the given time (e.g. before a bankruptcy event)
|
|
||||||
* This transaction occurred at some point roughly around the given time (e.g. on a specific day)
|
|
||||||
|
|
||||||
.. note:: It is assumed that the time feed for a notary is GPS/NaviStar time as defined by the atomic
|
|
||||||
clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.
|
|
275
docs/build/html/_sources/data-model.txt
vendored
@ -1,275 +0,0 @@
|
|||||||
Data model
|
|
||||||
==========
|
|
||||||
|
|
||||||
This article covers the data model: how *states*, *transactions* and *code contracts* interact with each other and
|
|
||||||
how they are represented in software.
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
We begin with the idea of a global ledger. In our model although the ledger is shared, it is not always the case that
|
|
||||||
transactions and ledger entries are globally visible. In cases where a set of transactions stays within a small subgroup of
|
|
||||||
users it should be possible to keep the relevant data purely within that group.
|
|
||||||
|
|
||||||
To ensure consistency in a global, shared system where not all data may be visible to all participants, we rely
|
|
||||||
heavily on secure hashes like SHA-256 to identify things. The ledger is defined as a set of immutable **states**, which
|
|
||||||
are created and destroyed by digitally signed **transactions**. Each transaction points to a set of states that it will
|
|
||||||
consume/destroy, these are called **inputs**, and contains a set of new states that it will create, these are called
|
|
||||||
**outputs**.
|
|
||||||
|
|
||||||
States contain arbitrary data, but they always contain at minimum a hash of the bytecode of a
|
|
||||||
**contract code** file, which is a program expressed in JVM byte code that runs sandboxed inside a Java virtual machine.
|
|
||||||
Contract code (or just "contracts" in the rest of this document) are globally shared pieces of business logic.
|
|
||||||
|
|
||||||
.. note:: In the current code dynamic loading of contracts is not implemented, so states currently point at
|
|
||||||
statically created object instances. This will change in the near future.
|
|
||||||
|
|
||||||
Contracts define a **verify function**, which is a pure function given the entire transaction as input. To be considered
|
|
||||||
valid, the transaction must be **accepted** by the verify function of every contract pointed to by the input and output
|
|
||||||
states.
|
|
||||||
|
|
||||||
Beyond inputs and outputs, transactions may also contain **commands**, small data packets that
|
|
||||||
the platform does not interpret itself but which can parameterise execution of the contracts. They can be thought of as
|
|
||||||
arguments to the verify function. Each command has a list of **public keys** associated with it. The platform ensures
|
|
||||||
that the transaction is signed by every key listed in the commands before the contracts start to execute. Thus, a verify
|
|
||||||
function can trust that all listed keys have signed the transaction but is responsible for verifying that any keys required
|
|
||||||
for the transaction to be valid from the verify function's perspective are included in the list. Public keys
|
|
||||||
may be random/identityless for privacy, or linked to a well known legal identity, for example via a
|
|
||||||
*public key infrastructure* (PKI).
|
|
||||||
|
|
||||||
.. note:: Linkage of keys with identities via a PKI is only partially implemented in the current code.
|
|
||||||
|
|
||||||
Commands are always embedded inside a transaction. Sometimes, there's a larger piece of data that can be reused across
|
|
||||||
many different transactions. For this use case, we have **attachments**. Every transaction can refer to zero or more
|
|
||||||
attachments by hash. Attachments are always ZIP/JAR files, which may contain arbitrary content. These files are
|
|
||||||
then exposed on the classpath and so can be opened by contract code in the same manner as any JAR resources
|
|
||||||
would be loaded.
|
|
||||||
|
|
||||||
.. note:: Attachments must be opened explicitly in the current code.
|
|
||||||
|
|
||||||
Note that there is nothing that explicitly binds together specific inputs, outputs, commands or attachments. Instead
|
|
||||||
it's up to the contract code to interpret the pieces inside the transaction and ensure they fit together correctly. This
|
|
||||||
is done to maximise flexibility for the contract developer.
|
|
||||||
|
|
||||||
Transactions may sometimes need to provide a contract with data from the outside world. Examples may include stock
|
|
||||||
prices, facts about events or the statuses of legal entities (e.g. bankruptcy), and so on. The providers of such
|
|
||||||
facts are called **oracles** and they provide facts to the ledger by signing transactions that contain commands they
|
|
||||||
recognise, or by creating signed attachments. The commands contain the fact and the signature shows agreement to that fact.
|
|
||||||
|
|
||||||
Time is also modelled as a fact, with the signature of a special kind of service called a **notary**. A notary is
|
|
||||||
a (very likely) decentralised service which fulfils the role that miners play in other blockchain systems:
|
|
||||||
notaries ensure only one transaction can consume any given output. Additionally they may verify a **timestamping
|
|
||||||
command** placed inside the transaction, which specifies a time window in which the transaction is considered
|
|
||||||
valid for notarisation. The time window can be open ended (i.e. with a start but no end or vice versa). In this
|
|
||||||
way transactions can be linked to the notary's clock.
|
|
||||||
|
|
||||||
It is possible for a single Corda network to have multiple competing notaries. Each state points to the notary that
|
|
||||||
controls it. Whilst a single transaction may only consume states if they are all controlled by the same notary,
|
|
||||||
a special type of transaction is provided that moves a state (or set of states) from one notary to another.
|
|
||||||
|
|
||||||
.. note:: Currently the platform code will not re-assign states to a single notary as needed for you, in case of
|
|
||||||
a mismatch. This is a future planned feature.
|
|
||||||
|
|
||||||
As the same terminology often crops up in different distributed ledger designs, let's compare this to other
|
|
||||||
systems you may be familiar with. The key differences are:
|
|
||||||
|
|
||||||
* Improved contract flexibility vs Bitcoin
|
|
||||||
* Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)
|
|
||||||
* No reliance on proof of work
|
|
||||||
* Re-use of existing sandboxing virtual machines
|
|
||||||
* Use of type safe GCd implementation languages
|
|
||||||
* Simplified auditing
|
|
||||||
|
|
||||||
Comparison with Bitcoin
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Similarities:
|
|
||||||
|
|
||||||
* The basic notion of immutable states that are consumed and created by transactions is the same.
|
|
||||||
* The notion of transactions having multiple inputs and outputs is the same. Bitcoin sometimes refers to the ledger
|
|
||||||
as the unspent transaction output set (UTXO set) as a result.
|
|
||||||
* Like in Bitcoin, a contract is pure function. Contracts do not have storage or the ability to interact with anything.
|
|
||||||
Given the same transaction, a contract's accept function always yields exactly the same result.
|
|
||||||
* Bitcoin output scripts are parameterised by the input scripts in the spending transaction. This is somewhat similar
|
|
||||||
to our notion of a *command*.
|
|
||||||
* Bitcoin has a global distributed notary service; that's the famous block chain. However, there is only one. Whilst
|
|
||||||
there is a notion of a "side chain", this isn't integrated with the core Bitcoin data model and thus adds large
|
|
||||||
amounts of additional complexity meaning in practice side chains are not used.
|
|
||||||
* Bitcoin transactions, like ours, refer to the states they consume by using a (txhash, index) pair. The Bitcoin
|
|
||||||
protocol calls these "outpoints". In our code they are known as ``StateRefs`` but the concept is identical.
|
|
||||||
* Bitcoin transactions have an associated timestamp (the time at which they are mined).
|
|
||||||
|
|
||||||
Differences:
|
|
||||||
|
|
||||||
* A Bitcoin transaction has a single, rigid data format. A "state" in Bitcoin is always a (quantity of bitcoin, script)
|
|
||||||
pair and cannot hold any other data. Some people have been known to try and hack around this limitation by embedding
|
|
||||||
data in semi-standardised places in the contract code so the data can be extracted through pattern matching, but this
|
|
||||||
is a poor approach. Our states can include arbitrary typed data.
|
|
||||||
* A Bitcoin transaction's acceptance is controlled only by the contract code in the consumed input states. In practice
|
|
||||||
this has proved limiting. Our transactions invoke not only input contracts but also the contracts of the outputs.
|
|
||||||
* A Bitcoin script can only be given a fixed set of byte arrays as the input. This means there's no way for a contract
|
|
||||||
to examine the structure of the entire transaction, which severely limits what contracts can do.
|
|
||||||
* Our contracts are Turing-complete and can be written in any ordinary programming language that targets the JVM.
|
|
||||||
* Our transactions and contracts get their time from an attached timestamp rather than a block. This is
|
|
||||||
important given that we use block-free conflict resolution algorithms. The timestamp can be arbitrarily precise.
|
|
||||||
* We use the term "contract" to refer to a bundle of business logic that may handle various different tasks, beyond
|
|
||||||
transaction verification. For instance, currently our contracts also include code for creating valid transactions
|
|
||||||
(this is often called "wallet code" in Bitcoin).
|
|
||||||
|
|
||||||
Comparison with Ethereum
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Similarities:
|
|
||||||
|
|
||||||
* Like Ethereum, code runs inside a relatively powerful virtual machine and can contain complex logic. Non-assembly
|
|
||||||
based programming languages can be used for contract programming.
|
|
||||||
* They are both intended for the modelling of many different kinds of financial contract.
|
|
||||||
|
|
||||||
Differences:
|
|
||||||
|
|
||||||
* The term "contract" in Ethereum refers to an *instantiation* of a program that is replicated and maintained by
|
|
||||||
every participating node. This instantiation is very much like an object in an OO program: it can receive and send
|
|
||||||
messages, update local storage and so on. In contrast, we use the term "contract" to refer to a set of functions, only
|
|
||||||
one of which is a part of keeping the system synchronised (the verify function). That function is pure and
|
|
||||||
stateless i.e. it may not interact with any other part of the system whilst executing.
|
|
||||||
* There is no notion of an "account", as there is in Ethereum.
|
|
||||||
* As contracts don't have any kind of mutable storage, there is no notion of a "message" as in Ethereum.
|
|
||||||
* Ethereum claims to be a platform not only for financial logic, but literally any kind of application at all. Our
|
|
||||||
platform considers non-financial applications to be out of scope.
|
|
||||||
|
|
||||||
Rationale for and tradeoffs in adopting a UTXO-style model
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
As discussed above, Corda uses the so-called "UTXO set" model (unspent transaction output). In this model, the database
|
|
||||||
does not track accounts or balances. Instead all database entries are immutable. An entry is either spent or not spent
|
|
||||||
but it cannot be changed. In Bitcoin, spentness is implemented simply as deletion – the inputs of an accepted transaction
|
|
||||||
are deleted and the outputs created.
|
|
||||||
|
|
||||||
This approach has some advantages and some disadvantages, which is why some platforms like Ethereum have tried
|
|
||||||
(or are trying) to abstract this choice away and support a more traditional account-like model. We have explicitly
|
|
||||||
chosen *not* to do this and our decision to adopt a UTXO-style model is a deliberate one. In the section below,
|
|
||||||
the rationale for this decision and its pros and cons of this choice are outlined.
|
|
||||||
|
|
||||||
Rationale
|
|
||||||
---------
|
|
||||||
|
|
||||||
Corda, in common with other blockchain-like platforms, is designed to bring parties to shared sets of data into
|
|
||||||
consensus as to the existence, content and allowable evolutions of those data sets. However, Corda is designed with the
|
|
||||||
explicit aim of avoiding, to the extent possible, the scalability and privacy implications that arise from those platforms'
|
|
||||||
decisions to adopt a global broadcast model.
|
|
||||||
|
|
||||||
Whilst the privacy implications of a global consensus model are easy to understand, the scalability implications are
|
|
||||||
perhaps more subtle, yet serious. In a consensus system, it is critical that all processors of a transaction reach
|
|
||||||
precisely the same conclusion as to its effects. In situations where two transactions may act on the same data set,
|
|
||||||
it means that the two transactions must be processed in the same *order* by all nodes. If this were not the case then it
|
|
||||||
would be possible to devise situations where nodes processed transactions in different orders and reached different
|
|
||||||
conclusions as to the state of the system. It is for this reason that systems like Ethereum effectively run
|
|
||||||
single-threaded, meaning the speed of the system is limited by the single-threaded performance of the slowest
|
|
||||||
machine on the network.
|
|
||||||
|
|
||||||
In Corda, we assume the data being processed represents financial agreements between identifiable parties and that these
|
|
||||||
institutions will adopt the system only if a significant number of such agreements can be managed by the platform.
|
|
||||||
As such, the system has to be able to support parallelisation of execution to the greatest extent possible,
|
|
||||||
whilst ensuring correct transaction ordering when two transactions seek to act on the same piece of shared state.
|
|
||||||
|
|
||||||
To achieve this, we must minimise the number of parties who need to receive and process copies of any given
|
|
||||||
transaction and we must minimise the extent to which two transactions seek to mutate (or supersede) any given piece
|
|
||||||
of shared state.
|
|
||||||
|
|
||||||
A key design decision, therefore, is what should be the most atomic unit of shared data in the system. This decision
|
|
||||||
also has profound privacy implications: the more coarsely defined the shared data units, the larger the set of
|
|
||||||
actors who will likely have a stake in its accuracy and who must process and observe any update to it.
|
|
||||||
|
|
||||||
This becomes most obvious when we consider two models for representing cash balances and payments.
|
|
||||||
|
|
||||||
A simple account model for cash would define a data structure that maintained a balance at a particular bank for each
|
|
||||||
"account holder". Every holder of a balance would need a copy of this structure and would thus need to process and
|
|
||||||
validate every payment transaction, learning about everybody else's payments and balances in the process.
|
|
||||||
All payments across that set of accounts would have to be single-threaded across the platform, limiting maximum
|
|
||||||
throughput.
|
|
||||||
|
|
||||||
A more sophisticated example might create a data structure per account holder.
|
|
||||||
But, even here, I would leak my account balance to anybody to whom I ever made
|
|
||||||
a payment and I could only ever make one payment at a time, for the same reasons above.
|
|
||||||
|
|
||||||
A UTXO model would define a data structure that represented an *instance* of a claim against the bank. An account
|
|
||||||
holder could hold *many* such instances, the aggregate of which would reveal their balance at that institution. However,
|
|
||||||
the account holder now only needs to reveal to their payee those instances consumed in making a payment to that payee.
|
|
||||||
This also means the payer could make several payments in parallel. A downside is that the model is harder to understand.
|
|
||||||
However, we consider the privacy and scalability advantages to overwhelm the modest additional cognitive load this places
|
|
||||||
on those attempting to learn the system.
|
|
||||||
|
|
||||||
In what follows, further advantages and disadvantages of this design decision are explored.
|
|
||||||
|
|
||||||
Pros
|
|
||||||
----
|
|
||||||
|
|
||||||
The UTXO model has these advantages:
|
|
||||||
|
|
||||||
* Immutable ledger entries gives the usual advantages that a more functional approach brings: it's easy to do analysis
|
|
||||||
on a static snapshot of the data and reason about the contents.
|
|
||||||
* Because there are no accounts, it's very easy to apply transactions in parallel even for high traffic legal entities
|
|
||||||
assuming sufficiently granular entries.
|
|
||||||
* Transaction ordering becomes trivial: it is impossible to mis-order transactions due to the reliance on hash functions
|
|
||||||
to identify previous states. There is no need for sequence numbers or other things that are hard to provide in a
|
|
||||||
fully distributed system.
|
|
||||||
* Conflict resolution boils down to the double spending problem, which places extremely minimal demands on consensus
|
|
||||||
algorithms (as the variable you're trying to reach consensus on is a set of booleans).
|
|
||||||
|
|
||||||
Cons
|
|
||||||
----
|
|
||||||
|
|
||||||
It also comes with some pretty serious complexities that in practice must be abstracted from developers:
|
|
||||||
|
|
||||||
* Representing numeric amounts using immutable entries is unnatural. For instance, if you receive $1000 and wish
|
|
||||||
to send someone $100, you have to consume the $1000 output and then create two more: a $100 for the recipient and
|
|
||||||
$900 back to yourself as change. The fact that this happens can leak private information to an observer.
|
|
||||||
* Because users do need to think in terms of balances and statements, you have to layer this on top of the
|
|
||||||
underlying ledger: you can't just read someone's balance out of the system. Hence, the "wallet" / position manager.
|
|
||||||
Experience from those who have developed wallets for Bitcoin and other systems is that they can be complex pieces of code,
|
|
||||||
although the bulk of wallets' complexity in public systems is handling the lack of finality (and key management).
|
|
||||||
* Whilst transactions can be applied in parallel, it is much harder to create them in parallel due to the need to
|
|
||||||
strictly enforce a total ordering.
|
|
||||||
|
|
||||||
With respect to parallel creation, if the user is single threaded this is fine, but in a more complex situation
|
|
||||||
where you might want to be preparing multiple transactions in flight this can prove a limitation – in
|
|
||||||
the worst case where you have a single output that represents all your value, this forces you to serialise
|
|
||||||
the creation of every transaction. If transactions can be created and signed very fast that's not a concern.
|
|
||||||
If there's only a single user, that's not a concern.
|
|
||||||
|
|
||||||
Both cases are typically true in the Bitcoin world, so users don't suffer from this much. In the context of a
|
|
||||||
complex business with a large pool of shared funds, in which creation of transactions may be very slow due to the
|
|
||||||
need to get different humans to approve a tx using a signing device, this could quickly lead to frustrating
|
|
||||||
conflicts where someone approves a transaction and then discovers that it has become a double spend and
|
|
||||||
they must sign again. In the absolute worst case you could get a form of human livelock.
|
|
||||||
|
|
||||||
The tricky part about solving these problems is that the simplest way to express a payment request
|
|
||||||
("send me $1000 to public key X") inherently results in you receiving a single output, which then can
|
|
||||||
prove insufficiently granular to be convenient. In the Bitcoin space Mike Hearn and Gavin Andresen designed "BIP 70"
|
|
||||||
to solve this: it's a simple binary format for requesting a payment and specifying exactly how you'd like to get paid,
|
|
||||||
including things like the shape of the transaction. It may seem that it's an over complex approach: could you not
|
|
||||||
just immediately respend the big output back to yourself in order to split it? And yes, you could, until you hit
|
|
||||||
scenarios like "the machine requesting the payment doesn't have the keys needed to spend it",
|
|
||||||
which turn out to be very common. So it's really more effective for a recipient to be able to say to the
|
|
||||||
sender, "here's the kind of transaction I want you to send me". The :doc:`flow framework <flow-state-machines>`
|
|
||||||
may provide a vehicle to make such negotiations simpler.
|
|
||||||
|
|
||||||
A further challenge is privacy. Whilst our goal of not sending transactions to nodes that don't "need to know"
|
|
||||||
helps, to verify a transaction you still need to verify all its dependencies and that can result in you receiving
|
|
||||||
lots of transactions that involve random third parties. The problems start when you have received lots of separate
|
|
||||||
payments and been careful not to make them linkable to your identity, but then you need to combine them all in a
|
|
||||||
single transaction to make a payment.
|
|
||||||
|
|
||||||
Mike Hearn wrote an article about this problem and techniques to minimise it in
|
|
||||||
`this article <https://medium.com/@octskyward/merge-avoidance-7f95a386692f>`_ from 2013. This article
|
|
||||||
coined the term "merge avoidance", which has never been implemented in the Bitcoin space,
|
|
||||||
although not due to lack of practicality.
|
|
||||||
|
|
||||||
A piece of future work for the wallet implementation will be to implement automated "grooming" of the wallet
|
|
||||||
to "reshape" outputs to useful/standardised sizes, for example, and to send outputs of complex transactions
|
|
||||||
back to their issuers for reissuance to "sever" long privacy-breaching chains.
|
|
||||||
|
|
||||||
Finally, it should be noted that some of the issues described here are not really "cons" of
|
|
||||||
the UTXO model; they're just fundamental. If you used many different anonymous accounts to preserve some privacy
|
|
||||||
and then needed to spend the contents of them all simultaneously, you'd hit the same problem, so it's not
|
|
||||||
something that can be trivially fixed with data model changes.
|
|
62
docs/build/html/_sources/flow-state-machines.txt
vendored
@ -7,9 +7,9 @@
|
|||||||
Writing flows
|
Writing flows
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This article explains our approach to modelling financial flows in code. It explains how the
|
This article explains our approach to modelling business processes and the lower level network protocols that implement
|
||||||
platform's state machine framework is used, and takes you through the code for a simple 2-party asset trading flow
|
them. It explains how the platform's flow framework is used, and takes you through the code for a simple
|
||||||
which is included in the source.
|
2-party asset trading flow which is included in the source.
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
@ -407,10 +407,13 @@ Implementing the buyer
|
|||||||
|
|
||||||
OK, let's do the same for the buyer side:
|
OK, let's do the same for the buyer side:
|
||||||
|
|
||||||
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
|
||||||
:language: kotlin
|
:language: kotlin
|
||||||
:start-after: DOCSTART 1
|
:start-after: DOCSTART 1
|
||||||
:end-before: DOCEND 1
|
:end-before: DOCEND 1
|
||||||
|
:dedent: 8
|
||||||
|
|
||||||
This code is longer but no more complicated. Here are some things to pay attention to:
|
This code is longer but no more complicated. Here are some things to pay attention to:
|
||||||
|
|
||||||
@ -440,16 +443,59 @@ stage in a piece of work. It is therefore typical to use singletons that subclas
|
|||||||
in one line when using Kotlin. Typical steps might be "Waiting for response from peer", "Waiting for signature to be
|
in one line when using Kotlin. Typical steps might be "Waiting for response from peer", "Waiting for signature to be
|
||||||
approved", "Downloading and verifying data" etc.
|
approved", "Downloading and verifying data" etc.
|
||||||
|
|
||||||
|
A flow might declare some steps with code inside the flow class like this:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 2
|
||||||
|
:end-before: DOCSTART 1
|
||||||
|
:dedent: 4
|
||||||
|
|
||||||
|
|
||||||
|
.. sourcecode:: java
|
||||||
|
|
||||||
|
private final ProgressTracker progressTracker = new ProgressTracker(
|
||||||
|
CONSTRUCTING_OFFER,
|
||||||
|
SENDING_OFFER_AND_RECEIVING_PARTIAL_TRANSACTION,
|
||||||
|
VERIFYING
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ProgressTracker.Step CONSTRUCTING_OFFER = new ProgressTracker.Step(
|
||||||
|
"Constructing proposed purchase order.");
|
||||||
|
private static final ProgressTracker.Step SENDING_OFFER_AND_RECEIVING_PARTIAL_TRANSACTION = new ProgressTracker.Step(
|
||||||
|
"Sending purchase order to seller for review, and receiving partially signed transaction from seller in return.");
|
||||||
|
private static final ProgressTracker.Step VERIFYING = new ProgressTracker.Step(
|
||||||
|
"Verifying signatures and contract constraints.");
|
||||||
|
|
||||||
Each step exposes a label. By default labels are fixed, but by subclassing ``RelabelableStep``
|
Each step exposes a label. By default labels are fixed, but by subclassing ``RelabelableStep``
|
||||||
you can make a step that can update its label on the fly. That's useful for steps that want to expose non-structured
|
you can make a step that can update its label on the fly. That's useful for steps that want to expose non-structured
|
||||||
progress information like the current file being downloaded. By defining your own step types, you can export progress
|
progress information like the current file being downloaded. By defining your own step types, you can export progress
|
||||||
in a way that's both human readable and machine readable.
|
in a way that's both human readable and machine readable.
|
||||||
|
|
||||||
Progress trackers are hierarchical. Each step can be the parent for another tracker. By altering the
|
Progress trackers are hierarchical. Each step can be the parent for another tracker. By altering the
|
||||||
``ProgressTracker.childrenFor[step] = tracker`` map, a tree of steps can be created. It's allowed to alter the hierarchy
|
``ProgressTracker.childrenFor`` map, a tree of steps can be created. It's allowed to alter the hierarchy
|
||||||
at runtime, on the fly, and the progress renderers will adapt to that properly. This can be helpful when you don't
|
at runtime, on the fly, and the progress renderers will adapt to that properly. This can be helpful when you don't
|
||||||
fully know ahead of time what steps will be required. If you _do_ know what is required, configuring as much of the
|
fully know ahead of time what steps will be required. If you *do* know what is required, configuring as much of the
|
||||||
hierarchy ahead of time is a good idea, as that will help the users see what is coming up.
|
hierarchy ahead of time is a good idea, as that will help the users see what is coming up. You can pre-configure
|
||||||
|
steps by overriding the ``Step`` class like this:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
|
||||||
|
:language: kotlin
|
||||||
|
:start-after: DOCSTART 3
|
||||||
|
:end-before: DOCEND 3
|
||||||
|
:dedent: 4
|
||||||
|
|
||||||
|
.. sourcecode:: java
|
||||||
|
|
||||||
|
private static final ProgressTracker.Step COMMITTING = new ProgressTracker.Step("Committing to the ledger.") {
|
||||||
|
@Nullable @Override public ProgressTracker childProgressTracker() {
|
||||||
|
return FinalityFlow.Companion.tracker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Every tracker has not only the steps given to it at construction time, but also the singleton
|
Every tracker has not only the steps given to it at construction time, but also the singleton
|
||||||
``ProgressTracker.UNSTARTED`` step and the ``ProgressTracker.DONE`` step. Once a tracker has become ``DONE`` its
|
``ProgressTracker.UNSTARTED`` step and the ``ProgressTracker.DONE`` step. Once a tracker has become ``DONE`` its
|
||||||
@ -469,7 +515,7 @@ overriding the ``flowTracker`` property (``getFlowTracker`` method in Java). If
|
|||||||
step in the parent flow automatically, if the parent is using tracking in the first place. The framework will also
|
step in the parent flow automatically, if the parent is using tracking in the first place. The framework will also
|
||||||
automatically set the current step to ``DONE`` for you, when the flow is finished.
|
automatically set the current step to ``DONE`` for you, when the flow is finished.
|
||||||
|
|
||||||
Because a flow may sometimes wish to configure the children in its progress hierarchy _before_ the sub-flow
|
Because a flow may sometimes wish to configure the children in its progress hierarchy *before* the sub-flow
|
||||||
is constructed, for sub-flows that always follow the same outline regardless of their parameters it's conventional
|
is constructed, for sub-flows that always follow the same outline regardless of their parameters it's conventional
|
||||||
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
|
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
|
||||||
the sub-flow to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
|
the sub-flow to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
|
||||||
|
@ -1,83 +1,83 @@
|
|||||||
Troubleshooting
|
Troubleshooting
|
||||||
===============
|
===============
|
||||||
|
|
||||||
IntelliJ issues
|
Milestone releases
|
||||||
---------------
|
------------------
|
||||||
|
|
||||||
Run configurations are missing
|
When you clone the corda or cordapp-template repos, they will default to the master branch. The master branch is being continuously developed upon, and its features may not align with the state of Corda as described in the docs. Additionally, the master branch of the CorDapp Template may break in response to changes in the main corda repo.
|
||||||
|
|
||||||
|
When developing on Corda, you should always check out the latest stable branch instead, by running ``git checkout release-M7``.
|
||||||
|
|
||||||
|
Java issues
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Outdated/non-Oracle JDKs
|
||||||
|
************************
|
||||||
|
|
||||||
|
Many users have faced issues when running versions of Java that are either outdated, or are produced by organisations other than Oracle (e.g. OpenJDK). The errors generated by these issues will not always clearly point to the JDK as the cause. If in doubt, check your JDK version by following the instructions `here <https://java.com/en/download/help/version_manual.xml>`_. You can download the latest version of Oracle's JDK `here <http://www.oracle.com/technetwork/java/javase/downloads/index.html>`_.
|
||||||
|
|
||||||
|
"Unresolved reference: javafx"
|
||||||
******************************
|
******************************
|
||||||
|
|
||||||
If you opened the Corda project using "Import" from the IntelliJ splash screen rather than using "Open" and then
|
JavaFX is not bundled with OpenJDK. If you are using OpenJDK and get an 'Unresolved reference: javafx' error, this means that you need to install OpenJFX. Do this by running ``sudo apt install openjfx``, and possibly ``sudo apt install libopenjfx-jav``.
|
||||||
importing the Gradle build system from the popup bubble, then a bug in IntelliJ will cause it to wipe and recreate
|
|
||||||
the ``.idea`` directory where the run configurations are stored. The fix is simple and doesn't require you to
|
|
||||||
re-import the project: just undelete the files! You can do that by either:
|
|
||||||
|
|
||||||
1. Running ``git checkout .idea/runConfigurations`` to restore that part of the tree to its normal state.
|
IDEA issues
|
||||||
2. Using the "Version Control" pane in IntelliJ to undelete the files via the GUI.
|
---------------
|
||||||
|
|
||||||
If IntelliJ complains about lack of an SDK
|
No source files are present
|
||||||
******************************************
|
***************************
|
||||||
|
|
||||||
If on attempting to open the project (including importing Gradle project), IntelliJ refuses because an SDK was not selected,
|
When opening a project in IDEA for the first time, you will need to build the project. You should see "Unlinked Gradle project?"
|
||||||
you may need to fix the project structure. Do this by following `these instructions <https://www.jetbrains.com/help/idea/2016.2/configuring-global-project-and-module-sdks.html>`_. The correct JDK is often found at a path such as ``jdk1.8.0_xx…/Contents/Home``
|
in a pop-up window in the top-right corner or in a popup alert window. It will also appear in the "Event Log" window which can be
|
||||||
|
opened by clicking on "Event Log" at the bottom right of the IDEA window. Find one of these links and click on "Import Gradle Project".
|
||||||
Ensure that you have the Project language level set at as 8. If you are having trouble selecting the correct JDK, the
|
|
||||||
JetBrains website offers the `following guidelines <https://intellij-support.jetbrains.com/hc/en-us/articles/206544879-Selecting-the-JDK-version-the-IDE-will-run-under>`_.
|
|
||||||
|
|
||||||
Kotlin issues
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Installation
|
|
||||||
************
|
|
||||||
|
|
||||||
There are two ways to configure Kotlin from IntelliJ. One way is via the initial project opening screen in which you will
|
|
||||||
need to use the ``Configure > Plugins`` tab. The other way is when you are in an open project, then you will need to
|
|
||||||
configure it via (on Mac) ``IntelliJ -> Preferences ...``, whereas on PC it is ``File -> Settings``. Select the plugins
|
|
||||||
bar, confirm that Kotlin is installed and up to date.
|
|
||||||
|
|
||||||
If you are having trouble installing Kotlin, first try upgrading the Kotlin plugin. At the time of writing, you can
|
|
||||||
confirm what is the latest version of the Kotlin plugin on `this page <https://plugins.jetbrains.com/plugin/6954>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Gradle issues
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Gradle within IntelliJ
|
|
||||||
**********************
|
|
||||||
|
|
||||||
After you have updated your code to the latest version from git, ensure that the gradle project is imported. Although
|
|
||||||
gradle is used via the command line, it is also integrated with IntelliJ in order for IntelliJ to determine dependencies
|
|
||||||
and index the project correctly.
|
|
||||||
|
|
||||||
When opening a project for the first time, you should see the "Unlinked Gradle project?" pop-up window in the IntelliJ top
|
|
||||||
right corner or in a popup alert window. If you miss this, it will also appear in the "Event Log" windows which can be
|
|
||||||
opened by clicking on "Event Log" at the bottom right of the IntelliJ window. Either way, click on "Import Gradle Project".
|
|
||||||
|
|
||||||
.. image:: resources/unlinked-gradle.png
|
.. image:: resources/unlinked-gradle.png
|
||||||
:height: 50 px
|
:height: 50 px
|
||||||
:width: 410 px
|
:width: 410 px
|
||||||
:alt: IntelliJ Gradle Prompt
|
:alt: IDEA Gradle Prompt
|
||||||
|
|
||||||
Wait for it to think and download the dependencies. After that you might have another popup titled "Unindexed remote maven repositories found." This is a general IntelliJ question and doesn't affect Corda, therefore you can decided to index them or not. Next click on the "green arrow" next to "All tests" pop-up on the top toolbar.
|
Wait for it to download the dependencies. You may then see another popup titled "Unindexed remote maven repositories found." This won't affect Corda,
|
||||||
|
so you can choose to leave them unindexed.
|
||||||
|
|
||||||
The code should build, the unit tests should show as all green.
|
If still have problems, the JetBrains website has more information on `here <https://www.jetbrains.com/help/idea/2016.2/working-with-gradle-projects.html>`_.
|
||||||
|
|
||||||
If still have problems, the JetBrains website has more information on `gradle here <https://www.jetbrains.com/help/idea/2016.2/working-with-gradle-projects.html>`_.
|
Run configurations are missing
|
||||||
|
******************************
|
||||||
|
|
||||||
Gradle via the CLI
|
If you opened the Corda project by clicking "Import Project" on the IDEA splash screen rather than clicking "Open", a bug
|
||||||
******************
|
in IDEA will cause it to wipe and recreate the ``.idea`` directory where the run configurations are stored. The fix is
|
||||||
|
simple and doesn't require you to re-import the project: just undelete the files! You can do that by either:
|
||||||
|
|
||||||
Gradle commands can also be run from the command line - further details of command line gradle can be found `here <https://docs.gradle.org/current/userguide/gradle_command_line.html>`_.
|
1. Running ``git checkout .idea/runConfigurations`` to redownload the files.
|
||||||
|
2. Using the "Version Control" pane in IDEA to undelete the files via the GUI.
|
||||||
|
|
||||||
Doing it without IntelliJ
|
IDEA complains about lack of an SDK
|
||||||
-------------------------
|
***************************************
|
||||||
|
|
||||||
If you don't want to explore or modify the code in a local IDE, you can also just use the command line and a text editor:
|
If IDEA refuses to open a project because an SDK has not been selected, you may need to fix the project structure. Do this by following `these instructions <https://www.jetbrains.com/help/idea/2016.2/configuring-global-project-and-module-sdks.html>`_. The correct JDK is often found on a path such as ``jdk1.8.0_xx…/Contents/Home``. Ensure that you have the Project language level set at 8.
|
||||||
|
|
||||||
* First run ``git clone https://github.com/corda/corda`` to download Corda core source code
|
If you are having trouble selecting the correct JDK, the JetBrains website provides the `following guidelines <https://intellij-support.jetbrains.com/hc/en-us/articles/206544879-Selecting-the-JDK-version-the-IDE-will-run-under>`_.
|
||||||
|
|
||||||
* Next ensure that you are in correct directory ``cd corda``
|
Kotlin plugin
|
||||||
|
*************
|
||||||
|
|
||||||
* Then you can run ``./gradlew test`` to run the unit tests.
|
There are two ways to configure Kotlin in IDEA:
|
||||||
|
|
||||||
* Finally remember to run ``git pull`` occasionally to upgrade the source code to the latest revision
|
1. Via the initial project opening screen, by using the ``Configure > Plugins`` tab.
|
||||||
|
2. From an open IDEA project, by clicking ``IDEA -> Preferences ...`` (on OS X) or ``File -> Settings`` (on Windows). Select the Plugins bar to confirm that Kotlin is installed and up-to-date.
|
||||||
|
|
||||||
|
If you are still having trouble installing Kotlin, first try upgrading the Kotlin plugin. At the time of writing, you can
|
||||||
|
identify the latest version of the Kotlin plugin on `this page <https://plugins.jetbrains.com/plugin/6954>`_.
|
||||||
|
|
||||||
|
"Unable to make the module: related gradle configuration was not found. Please, re-import the Gradle project and try again"
|
||||||
|
***************************************************************************************************************************
|
||||||
|
|
||||||
|
This can usually be solved by updating IDEA. Check that you have the latest version `here <https://www.jetbrains.com/idea/download/>`_.
|
||||||
|
|
||||||
|
Other common issues
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
“xterm: command not found”
|
||||||
|
**************************
|
||||||
|
|
||||||
|
On some machines, running the samples requires xterm. You can download it `here <http://invisible-island.net/xterm/#download>`_.
|
120
docs/build/html/_sources/getting-set-up.txt
vendored
@ -1,83 +1,103 @@
|
|||||||
Getting set up
|
Getting set up
|
||||||
==============
|
==============
|
||||||
|
|
||||||
We have tried to make access to Corda as relatively simple as possible, using industry standard established tools.
|
Software requirements
|
||||||
Although it is possible to replace any of the recommendations below, we will find it a lot easier to support your efforts
|
---------------------
|
||||||
if you follow our guidelines. Saying that, we are also interested in problems that arise due to different configurations.
|
|
||||||
|
|
||||||
A JVM
|
Corda uses industry-standard tools to make set-up as simple as possible. Following the software recommendations below will
|
||||||
-----
|
minimize the number of errors you encounter, and make it easier for others to provide support. However, if you do use other tools,
|
||||||
|
we're interested to hear about any issues that arise.
|
||||||
|
|
||||||
Corda runs in a JVM and is written predominantly in Kotlin with some example use cases demonstrated in Java that we have
|
JVM
|
||||||
incorporated to demonstrate that Kotlin and Java can work seamlessly together. We recommend the most recent production
|
~~~
|
||||||
version of Java 8. The JDK can be obtained `from Oracle <http://www.oracle.com/technetwork/java/javase/downloads/index.html>`_.
|
|
||||||
Other implementations of the JVM are not actively supported, but as mentioned, we are interested in finding out any issues you
|
|
||||||
do have with them.
|
|
||||||
|
|
||||||
.. note:: If you are using a JVM implementation other than Oracle's you may get errors similar to ``Unresolved reference: javafx``.
|
Corda is written in Kotlin and runs in a JVM. We develop against Oracle JDK 8, and other JVM implementations are not actively
|
||||||
This means JavaFX is not bundled with the JVM and you will need to install it separately (e.g. OpenJFX is needed
|
supported. Oracle JDK 8 can be obtained directly from
|
||||||
with OpenJDK).
|
`Oracle <http://www.oracle.com/technetwork/java/javase/downloads/index.html>`_. Installation instructions are
|
||||||
|
available for `Windows <http://docs.oracle.com/javase/8/docs/technotes/guides/install/windows_jdk_install.html#CHDEBCCJ>`_,
|
||||||
|
`Linux <http://docs.oracle.com/javase/8/docs/technotes/guides/install/linux_jdk.html#BJFGGEFG>`_ and
|
||||||
|
`OS X <http://docs.oracle.com/javase/8/docs/technotes/guides/install/mac_jdk.html#CHDBADCG>`_.
|
||||||
|
|
||||||
IntelliJ
|
Please ensure that you keep your Oracle JDK installation updated to the latest version while working with Corda.
|
||||||
--------
|
Even earlier versions of JDK 8 versions can cause cryptic errors.
|
||||||
We strongly recommend the use of IntelliJ's Development Environment known as IDEA. Download it for free from
|
|
||||||
`JetBrains <https://www.jetbrains.com/idea/download/>`_. The primary reason we recommend this particular IDE is that it integrates
|
|
||||||
very well with our choice of language for Corda, "Kotlin", as JetBrains also support the development of Kotlin.
|
|
||||||
|
|
||||||
.. warning:: When opening the Corda project for the first time from the IntelliJ splash screen, please use "Open"
|
If you do choose to use OpenJDK instead of Oracle's JDK, you will also need to install OpenJFX.
|
||||||
and then agree to import the Gradle project from the popup bubble. Don't pick "Import" on the splash screen,
|
|
||||||
because a bug in IntelliJ will cause the pre-packaged run configurations to be erased. If you see this warning
|
|
||||||
too late, it's no problem, just use ``git checkout .idea/runConfiguration`` or the version control tab in IntelliJ
|
|
||||||
to undelete the files.
|
|
||||||
|
|
||||||
|
Additional troubleshooting information can be found `here <https://docs.corda.net/getting-set-up-fault-finding.html#java-issues>`_.
|
||||||
|
|
||||||
Kotlin
|
Kotlin
|
||||||
------
|
~~~~~~
|
||||||
Kotlin is available as a downloadable plugin to IntelliJ. Refer to IntelliJ's instructions on
|
|
||||||
`getting Started with Kotlin and IntelliJ <https://kotlinlang.org/docs/tutorials/getting-started.html>`_. Additionally,
|
|
||||||
if you would like to start getting to grips with the Kotlin language, then we strongly recommend you work through some
|
|
||||||
of the tutorials (known as "koans") as well. Also see our :doc:`further-notes-on-kotlin`.
|
|
||||||
|
|
||||||
|
Applications on Corda (CorDapps) can be written in any JVM-targeting language. However, Corda itself and most of the samples
|
||||||
|
are written in Kotlin. If you're unfamiliar with Kotlin, there is an official `getting started guide <https://kotlinlang.org/docs/tutorials/>`_.
|
||||||
|
See also our :doc:`further-notes-on-kotlin`.
|
||||||
|
|
||||||
Version control via Git
|
IDE
|
||||||
-----------------------
|
~~~
|
||||||
|
|
||||||
We use git to version control Corda. The authoritative place to obtain git is from the main `git website <https://git-scm.com/downloads>`_
|
We strongly recommend the use of IntelliJ IDEA as an IDE, primarily due to the strength of its Kotlin integration. The free Community
|
||||||
but it may be the case that your operating system provides git with a supported utility (e.g. for Apple, git is provided along
|
Edition can be downloaded from `JetBrains <https://www.jetbrains.com/idea/download/>`_.
|
||||||
with XCode - their free development environment). If this is the case, we would recommend you obtain git via that
|
|
||||||
supported route.
|
|
||||||
|
|
||||||
You will need the command line package installed which you can then use natively (via the command line) or via IntelliJ
|
Please make sure that you're running the latest version of IDEA, as older versions have been known to have problems integrating with Gradle,
|
||||||
(in which case you may need to configure IntelliJ to recognise where git has been installed on your system). IntelliJ and
|
the build tool used by Corda.
|
||||||
git configuration are quite seamless although the first time you use it, you will have to configure IntelliJ the location
|
|
||||||
of your git command installation. More details regarding this can be found
|
You'll also want to install the Kotlin IDEA plugin by following the instructions
|
||||||
on the `JetBrains website <https://www.jetbrains.com/help/idea/2016.2/using-git-integration.html>`_
|
`here <https://kotlinlang.org/docs/tutorials/getting-started.html>`_.
|
||||||
|
|
||||||
|
Additional troubleshooting information can be found `here <https://docs.corda.net/getting-set-up-fault-finding.html#idea-issues>`_.
|
||||||
|
|
||||||
|
Git
|
||||||
|
~~~
|
||||||
|
|
||||||
|
We use git to version-control Corda. Instructions on installing git can be found
|
||||||
|
`here <https://git-scm.com/book/en/v2/Getting-Started-Installing-Git>`_.
|
||||||
|
|
||||||
|
Following these instructions will give you access to git via the command line. It can also be useful to control git via IDEA. Instructions
|
||||||
|
for doing so can be found on the `JetBrains website <https://www.jetbrains.com/help/idea/2016.2/using-git-integration.html>`_.
|
||||||
|
|
||||||
Gradle
|
Gradle
|
||||||
------
|
~~~~~~
|
||||||
|
|
||||||
Gradle is our primary means of building Corda and managing dependencies. IntelliJ has its own view of this and occasionally
|
We use Gradle as the build tool for Corda. However, you do not need to install Gradle itself, as a wrapper is provided.
|
||||||
may need to be resynced from time to time. This can be done within IntelliJ by pressing the "gradle refresh" icon located
|
|
||||||
on the gradle tab (generally found on the right hand side), or by following the gradle commands specific for the task you
|
|
||||||
are performing (details expounded later). Whenever prompted about gradle, accept the defaults suggested by IntelliJ.
|
|
||||||
|
|
||||||
|
The wrapper can be run from the command line by using ``./gradlew [taskName]`` on OS X/Linux, or ``gradlew.bat [taskName]`` on Windows.
|
||||||
|
|
||||||
Corda source code
|
Corda source code
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
You can check out the Corda platform source code from this repository:
|
The Corda platform source code is available here:
|
||||||
|
|
||||||
https://github.com/corda/corda.git
|
https://github.com/corda/corda.git
|
||||||
|
|
||||||
and a template app that you can use as a basis for experimenting with app development from:
|
and a basic CorDapp that you can use as the basis for your own CorDapps is available here:
|
||||||
|
|
||||||
https://github.com/corda/cordapp-template.git
|
https://github.com/corda/cordapp-template.git
|
||||||
|
|
||||||
You can catch up with the latest code by selecting "VCS -> Update Project" in the IntelliJ menu.
|
You can clone both of these repos to your local machine by running the command ``git clone [repo URL]``.
|
||||||
|
|
||||||
|
By default, both repos will be on the ``master`` branch. However, this is an unstable development branch. You should check
|
||||||
|
out the latest milestone release (currently Milestone 7) instead by running ``git checkout release-M7``.
|
||||||
|
|
||||||
Troubleshooting
|
Opening Corda/CorDapps in IDEA
|
||||||
---------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
See :doc:`getting-set-up-fault-finding`, or get in touch with us either on the `forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.
|
When opening a Corda project for the first time from the IDEA splash screen, please click "Open" rather than "Import Project",
|
||||||
|
and then import the Gradle project by clicking "Import Gradle project" in the popup bubble on the lower right-hand side of the screen.
|
||||||
|
If you instead pick "Import Project" on the splash screen, a bug in IDEA will cause Corda's pre-packaged run configurations to be erased.
|
||||||
|
|
||||||
|
If you see this warning too late, that's not a problem - just use ``git checkout .idea/runConfiguration`` or the version control tab in
|
||||||
|
IDEA to undelete the files.
|
||||||
|
|
||||||
|
IDEA's build of the project may need to be resynced from time to time. This can be done from within IDEA by going to "View" -> "Tool Windows" -> "Gradle"
|
||||||
|
and clicking "Refresh all Gradle projects". Whenever prompted about Gradle, accept the defaults suggested by IDEA.
|
||||||
|
|
||||||
|
Next steps
|
||||||
|
----------
|
||||||
|
|
||||||
|
The best way to check that everything is working fine is by :doc:`running-the-demos`.
|
||||||
|
|
||||||
|
Once you have these demos running, you may be interested in writing your own CorDapps, in which case you should refer to
|
||||||
|
:doc:`tutorial-cordapp`.
|
||||||
|
|
||||||
|
If you encounter any issues, please see the :doc:`getting-set-up-fault-finding` page, or get in touch with us on the
|
||||||
|
`forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.
|
30
docs/build/html/_sources/index.txt
vendored
@ -14,17 +14,18 @@ they become more familiar with Corda, readers with a technical background will a
|
|||||||
which describes the platform's envisioned end-state.
|
which describes the platform's envisioned end-state.
|
||||||
|
|
||||||
Corda is designed so that developers can easily extend its functionality by writing CorDapps
|
Corda is designed so that developers can easily extend its functionality by writing CorDapps
|
||||||
(**Cor**\ da **D**\ istributed **App**\ lication\ **s**\ ). An example CorDapp is available on
|
(**Cor**\ da **D**\ istributed **App**\ lication\ **s**\ ). Some example CorDapps are available in the Corda repo's
|
||||||
|
`samples <https://github.com/corda/corda/tree/master/samples>`_ directory. To run these yourself, make
|
||||||
|
sure you follow the instructions in :doc:`getting-set-up`, then go to
|
||||||
|
:doc:`running-the-demos`.
|
||||||
|
|
||||||
|
If, after running the demos, you're interested in writing your own CorDapps, a template CorDapp is available on
|
||||||
`Github <https://github.com/corda/cordapp-template>`_. To get it running, follow the instructions in the
|
`Github <https://github.com/corda/cordapp-template>`_. To get it running, follow the instructions in the
|
||||||
`readme <https://github.com/corda/cordapp-template/blob/master/README.md>`_, or watch the
|
`readme <https://github.com/corda/cordapp-template/blob/master/README.md>`_, or watch the
|
||||||
`Corda Developers Tutorial <https://vimeo.com/192797322/aab499b152>`_.
|
`Corda Developers Tutorial <https://vimeo.com/192797322/aab499b152>`_.
|
||||||
|
|
||||||
Additional CorDapp samples are available in the Corda repo's `samples <https://github.com/corda/corda/tree/master/samples>`_
|
|
||||||
directory. These are sophisticated CorDapps that implement more complex functionality. You can find directions for
|
|
||||||
running these samples `here <https://docs.corda.net/running-the-demos.html>`_.
|
|
||||||
|
|
||||||
From there, you'll be in a position to start extending the example CorDapp yourself (e.g. by writing new states, contracts,
|
From there, you'll be in a position to start extending the example CorDapp yourself (e.g. by writing new states, contracts,
|
||||||
and/or flows). For this, you'll want to refer to this docsite, and to the `tutorials <https://docs.corda.net/#tutorials>`_
|
and/or flows). For this, you'll want to refer to this docsite, and to the `tutorials <https://docs.corda.net/tutorial-contract.html>`_
|
||||||
in particular. If you get stuck, get in touch on `Slack <https://slack.corda.net/>`_ or the `forum <https://discourse.corda.net/>`_.
|
in particular. If you get stuck, get in touch on `Slack <https://slack.corda.net/>`_ or the `forum <https://discourse.corda.net/>`_.
|
||||||
|
|
||||||
Once you're familiar with Corda and CorDapp development, we'd encourage you to get involved in the development of the
|
Once you're familiar with Corda and CorDapp development, we'd encourage you to get involved in the development of the
|
||||||
@ -50,11 +51,15 @@ Documentation Contents:
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Key concepts
|
:caption: Key concepts
|
||||||
|
|
||||||
data-model
|
key-concepts
|
||||||
transaction-data-types
|
key-concepts-ecosystem
|
||||||
merkle-trees
|
key-concepts-data-model
|
||||||
consensus
|
key-concepts-core-types
|
||||||
clauses
|
key-concepts-financial-model
|
||||||
|
key-concepts-flow-framework
|
||||||
|
key-concepts-consensus-notaries
|
||||||
|
key-concepts-vault
|
||||||
|
key-concepts-security-model
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
@ -100,6 +105,8 @@ Documentation Contents:
|
|||||||
:caption: Other
|
:caption: Other
|
||||||
|
|
||||||
network-simulator
|
network-simulator
|
||||||
|
clauses
|
||||||
|
merkle-trees
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
@ -119,6 +126,7 @@ Documentation Contents:
|
|||||||
release-notes
|
release-notes
|
||||||
codestyle
|
codestyle
|
||||||
building-the-docs
|
building-the-docs
|
||||||
|
further-notes-on-kotlin
|
||||||
publishing-corda
|
publishing-corda
|
||||||
azure-vm
|
azure-vm
|
||||||
|
|
||||||
|
163
docs/build/html/_sources/key-concepts-consensus-notaries.txt
vendored
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
Consensus and notaries
|
||||||
|
======================
|
||||||
|
|
||||||
|
A notary is a service that provides transaction ordering and timestamping.
|
||||||
|
|
||||||
|
Notaries are expected to be composed of multiple mutually distrusting parties who use a standard consensus algorithm.
|
||||||
|
Notaries are identified by and sign with :ref:`composite-keys`. Notaries accept transactions submitted to them for processing
|
||||||
|
and either return a signature over the transaction, or a rejection error that states that a double spend attempt has occurred.
|
||||||
|
|
||||||
|
Corda has "pluggable" notary services to improve privacy, scalability, legal-system compatibility and algorithmic agility.
|
||||||
|
The platform currently provides validating and non-validating notaries, and a distributed RAFT implementation.
|
||||||
|
|
||||||
|
Consensus model
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The fundamental unit of consensus in Corda is the **state**. Consensus can be divided into two parts:
|
||||||
|
|
||||||
|
1. Consensus over state **validity** -- parties can reach certainty that a transaction is accepted by the contracts pointed
|
||||||
|
to by the input and output states, and has all the required signatures. This is achieved by parties independently running
|
||||||
|
the same contract code and validation logic (as described in :doc:`data model <data-model>`)
|
||||||
|
|
||||||
|
2. Consensus over state **uniqueness** -- parties can reach certainty the output states created in a transaction are the
|
||||||
|
unique successors to the input states consumed by that transaction (in other words -- an input state has not been previously
|
||||||
|
consumed)
|
||||||
|
|
||||||
|
.. note:: The current model is still a **work in progress** and everything described in this article can and is likely to change
|
||||||
|
|
||||||
|
Notary
|
||||||
|
------
|
||||||
|
|
||||||
|
A **notary** is an authority responsible for attesting that for a given transaction, it has not signed another transaction
|
||||||
|
consuming any of the same input states. Every **state** has an appointed notary:
|
||||||
|
|
||||||
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for [ContractState] containing additional platform-level state information.
|
||||||
|
* This is the definitive state that is stored on the ledger and used in transaction outputs
|
||||||
|
*/
|
||||||
|
data class TransactionState<out T : ContractState>(
|
||||||
|
/** The custom contract state */
|
||||||
|
val data: T,
|
||||||
|
/** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
|
||||||
|
val notary: Party) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Transactions are signed by a notary to ensure their input states are **valid** (apart from *issue* transactions, containing no input states).
|
||||||
|
Furthermore, when using a validating notary, a transaction is only valid if all its dependencies are also valid.
|
||||||
|
|
||||||
|
.. note:: The notary is a logical concept and can itself be a distributed entity, potentially a cluster maintained by mutually distrusting parties
|
||||||
|
|
||||||
|
When the notary is requested to sign a transaction, it either signs it, attesting that the outputs are the **unique**
|
||||||
|
successors of the inputs, or provides conflict information for any input state that has been consumed by another transaction
|
||||||
|
it has already signed. In doing so, the notary provides the point of finality in the system. Until the notary signature
|
||||||
|
is obtained, parties cannot be sure that an equally valid, but conflicting, transaction will not be regarded as confirmed.
|
||||||
|
After the signature is obtained, the parties know that the inputs to this transaction have been uniquely consumed by this transaction.
|
||||||
|
Hence, it is the point at which we can say finality has occurred.
|
||||||
|
|
||||||
|
Multiple notaries
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
More than one notary can exist in a network. This gives the following benefits:
|
||||||
|
|
||||||
|
* **Custom behaviour**. We can have both validating and privacy preserving Notaries -- parties can make a choice based
|
||||||
|
on their specific requirements.
|
||||||
|
* **Load balancing**. Spreading the transaction load over multiple notaries will allow higher transaction throughput in
|
||||||
|
the platform overall
|
||||||
|
* **Low latency**. Latency could be minimised by choosing a notary physically closer the transacting parties
|
||||||
|
|
||||||
|
Changing notaries
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A transaction should only be signed by a notary if all of its input states point to the same notary.
|
||||||
|
In cases where a transaction involves states controlled by multiple notaries, the states first have to be repointed to the same notary.
|
||||||
|
This is achieved by using a special type of transaction whose sole output state is identical to its sole input state except for its designated notary.
|
||||||
|
Ensuring that all input states point to the same notary is the responsibility of each involved party
|
||||||
|
(it is another condition for an output state of the transaction to be **valid**)
|
||||||
|
|
||||||
|
To change the notary for an input state, use the ``NotaryChangeFlow``. For example:
|
||||||
|
|
||||||
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
fun changeNotary(originalState: StateAndRef<ContractState>,
|
||||||
|
newNotary: Party): StateAndRef<ContractState> {
|
||||||
|
val flow = NotaryChangeFlow.Instigator(originalState, newNotary)
|
||||||
|
return subFlow(flow)
|
||||||
|
}
|
||||||
|
|
||||||
|
The flow will:
|
||||||
|
|
||||||
|
1. Construct a transaction with the old state as the input and the new state as the output
|
||||||
|
|
||||||
|
2. Obtain signatures from all *participants* (a participant is any party that is able to consume this state in a valid transaction, as defined by the state itself)
|
||||||
|
|
||||||
|
3. Obtain the *old* notary signature
|
||||||
|
|
||||||
|
4. Record and distribute the final transaction to the participants so that everyone possesses the new state
|
||||||
|
|
||||||
|
.. note:: Eventually, changing notaries will be handled automatically on demand.
|
||||||
|
|
||||||
|
Validation
|
||||||
|
----------
|
||||||
|
|
||||||
|
One of the design decisions for a notary is whether or not to **validate** a transaction before accepting it.
|
||||||
|
|
||||||
|
If a transaction is not checked for validity, it opens the platform to "denial of state" attacks, where anyone can build an invalid transaction consuming someone else's states and submit it to the notary to get the states "blocked".
|
||||||
|
However, if the transaction is validated, this requires the notary to be able to see the full contents of the transaction in question and its dependencies.
|
||||||
|
This is an obvious privacy leak.
|
||||||
|
|
||||||
|
The platform is flexible and currently supports both validating and non-validating notary implementations -- a party can select which one to use based on its own privacy requirements.
|
||||||
|
|
||||||
|
.. note:: In the non-validating model, the "denial of state" attack is partially alleviated by requiring the calling
|
||||||
|
party to authenticate and storing its identity for the request. The conflict information returned by the notary
|
||||||
|
specifies the consuming transaction ID along with the identity of the party that had created the transaction. If the
|
||||||
|
conflicting transaction is valid, the current one is aborted; if not, a dispute can be raised and the input states
|
||||||
|
of the conflicting invalid transaction are "un-committed" (via a legal process).
|
||||||
|
|
||||||
|
.. note:: At present, all notaries can see the entire contents of a submitted transaction. A future piece of work
|
||||||
|
will enable the processing of :doc:`merkle-trees`, thus providing data hiding of sensitive information.
|
||||||
|
|
||||||
|
Timestamping
|
||||||
|
------------
|
||||||
|
|
||||||
|
A notary can also act as a *timestamping authority*, verifying the transaction timestamp command.
|
||||||
|
|
||||||
|
For a timestamp to be meaningful, its implications must be binding on the party requesting it.
|
||||||
|
A party can obtain a timestamp signature in order to prove that some event happened *before*, *on*, or *after* a particular point in time.
|
||||||
|
However, if the party is not also compelled to commit to the associated transaction, it has a choice of whether or not to reveal this fact until some point in the future.
|
||||||
|
As a result, we need to ensure that the notary either has to also sign the transaction within some time tolerance,
|
||||||
|
or perform timestamping *and* notarisation at the same time, which is the chosen behaviour for this model.
|
||||||
|
|
||||||
|
There will never be exact clock synchronisation between the party creating the transaction and the notary.
|
||||||
|
This is not only due to physics, network latencies, etc. but also because between inserting the command and getting the
|
||||||
|
notary to sign there may be many other steps, like sending the transaction to other parties involved in the trade, or
|
||||||
|
even requesting human sign-off. Thus the time observed by the notary may be quite different to the time observed by the
|
||||||
|
party creating the transaction.
|
||||||
|
|
||||||
|
For this reason, times in transactions are specified as time *windows*, not absolute times.
|
||||||
|
In a distributed system there can never be "true time", only an approximation of it. Time windows can be
|
||||||
|
open-ended (i.e. specify only one of "before" and "after") or they can be fully bounded. If a time window needs to
|
||||||
|
be converted to an absolute time (e.g. for display purposes), there is a utility method on ``Timestamp`` to
|
||||||
|
calculate the mid point.
|
||||||
|
|
||||||
|
In this way, we express the idea that the *true value* of the fact "the current time" is actually unknowable. Even when both before and
|
||||||
|
after times are included, the transaction could have occurred at any point between those two timestamps. Here,
|
||||||
|
"occurrence" could mean the execution date, the value date, the trade date etc ... The notary doesn't care what precise
|
||||||
|
meaning the timestamp has to the contract.
|
||||||
|
|
||||||
|
By creating a range that can be either closed or open at one end, we allow all of the following facts to be modelled:
|
||||||
|
|
||||||
|
* This transaction occurred at some point after the given time (e.g. after a maturity event)
|
||||||
|
* This transaction occurred at any time before the given time (e.g. before a bankruptcy event)
|
||||||
|
* This transaction occurred at some point roughly around the given time (e.g. on a specific day)
|
||||||
|
|
||||||
|
.. note:: It is assumed that the time feed for a notary is GPS/NaviStar time as defined by the atomic
|
||||||
|
clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.
|
||||||
|
|
||||||
|
Also see section 7 of the `Technical white paper`_ which covers this topic in significantly more depth.
|
||||||
|
|
||||||
|
.. _`Technical white paper`: _static/corda-technical-whitepaper.pdf
|
||||||
|
|
@ -1,73 +1,30 @@
|
|||||||
Data types
|
Core types
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Corda provides a large standard library of data types used in financial transactions and contract state objects.
|
Corda provides a large standard library of data types used to represent the :doc:`key-concepts-data-model` previously described.
|
||||||
These provide a common language for states and contracts.
|
In addition, there are a series of helper libraries which provide date manipulation, maths and cryptography functions.
|
||||||
|
|
||||||
Amount
|
State and References
|
||||||
------
|
--------------------
|
||||||
|
State objects contain mutable data which we would expect to evolve over the lifetime of a contract.
|
||||||
|
|
||||||
The `Amount <api/kotlin/corda/net.corda.core.contracts/-amount/index.html>`_ class is used to represent an amount of some
|
A reference to a state in the ledger (whether it has been consumed or not) is represented with a ``StateRef`` object.
|
||||||
fungible asset. It is a generic class which wraps around a type used to define the underlying product, called
|
If the state ref has been looked up from storage, you will have a ``StateAndRef`` which is simply a ``StateRef`` plus the data.
|
||||||
the *token*. For instance it can be the standard JDK type ``Currency``, or an ``Issued`` instance, or this can be
|
|
||||||
a more complex type such as an obligation contract issuance definition (which in turn contains a token definition
|
|
||||||
for whatever the obligation is to be settled in).
|
|
||||||
|
|
||||||
.. note:: Fungible is used here to mean that instances of an asset is interchangeable for any other identical instance,
|
|
||||||
and that they can be split/merged. For example a £5 note can reasonably be exchanged for any other £5 note, and a
|
|
||||||
£10 note can be exchanged for two £5 notes, or vice-versa.
|
|
||||||
|
|
||||||
Here are some examples:
|
|
||||||
|
|
||||||
.. container:: codeset
|
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
|
||||||
|
|
||||||
// A quantity of some specific currency like pounds, euros, dollars etc.
|
|
||||||
Amount<Currency>
|
|
||||||
// A quantity of currency that is issued by a specific issuer, for instance central bank vs other bank dollars
|
|
||||||
Amount<Issued<Currency>>
|
|
||||||
// A quantity of obligations to deliver currency of any issuer.
|
|
||||||
Amount<Obligation.State<Currency>>
|
|
||||||
|
|
||||||
``Amount`` represents quantities as integers. For currencies the quantity represents pennies, cents or whatever
|
|
||||||
else the smallest integer amount for that currency is. You cannot use ``Amount`` to represent negative quantities
|
|
||||||
or fractional quantities: if you wish to do this then you must use a different type e.g. ``BigDecimal``. ``Amount``
|
|
||||||
defines methods to do addition and subtraction and these methods verify that the tokens on both sides of the operator
|
|
||||||
are equal (these are operator overloads in Kotlin and can be used as regular methods from Java). There are also
|
|
||||||
methods to do multiplication and division by integer amounts.
|
|
||||||
|
|
||||||
State
|
|
||||||
-----
|
|
||||||
|
|
||||||
A Corda contract is composed of three parts; the executable code, the legal prose, and the state objects that represent
|
|
||||||
the details of a specific deal or asset (see :doc:`data-model` for further detail). In relational database terms
|
|
||||||
a state is like a row in a database. A reference to a state in the ledger (whether it has been consumed or not)
|
|
||||||
is represented with a ``StateRef`` object. If the state ref has been looked up from storage, you will have a
|
|
||||||
``StateAndRef`` which is simply a ``StateRef`` plus the data.
|
|
||||||
|
|
||||||
The ``ContractState`` type is an interface that all states must implement. A ``TransactionState`` is a simple
|
The ``ContractState`` type is an interface that all states must implement. A ``TransactionState`` is a simple
|
||||||
container for a ``ContractState`` (the custom data used by a contract program) and additional platform-level state
|
container for a ``ContractState`` (the custom data used by a contract program) and additional platform-level state
|
||||||
information, such as the *notary* pointer (see :doc:`consensus`).
|
information, such as the *notary* pointer (see :doc:`key-concepts-consensus-notaries`).
|
||||||
|
|
||||||
A number of interfaces then extend ``ContractState``, representing standardised functionality for common kinds
|
A number of interfaces then extend ``ContractState``, representing standardised functionality for common kinds
|
||||||
of state:
|
of state such as:
|
||||||
|
|
||||||
``OwnableState``
|
``OwnableState``
|
||||||
A state which has an owner (represented as a ``PublicKey``, discussed later). Exposes the owner and a function
|
A state which has an owner (represented as a ``CompositeKey``, discussed later). Exposes the owner and a function
|
||||||
for replacing the owner e.g. when an asset is sold.
|
for replacing the owner e.g. when an asset is sold.
|
||||||
|
|
||||||
``LinearState``
|
``SchedulableState``
|
||||||
A state which links back to its previous state, creating a thread of states over time. A linear state is
|
A state to indicate whether there is some activity to be performed at some future point in time with respect to this
|
||||||
useful when modelling an indivisible/non-fungible thing like a specific deal, or an asset that can't be
|
contract, what that activity is and at what point in time it should be initiated.
|
||||||
split (like a rare piece of art).
|
|
||||||
|
|
||||||
``DealState``
|
|
||||||
A LinearState representing an agreement between two or more parties. Intended to simplify implementing generic
|
|
||||||
flows that manipulate many agreement types.
|
|
||||||
|
|
||||||
``FixableDealState``
|
|
||||||
A deal state, with further functions exposed to support fixing of interest rates.
|
|
||||||
|
|
||||||
NamedByHash and UniqueIdentifier
|
NamedByHash and UniqueIdentifier
|
||||||
--------------------------------
|
--------------------------------
|
||||||
@ -81,30 +38,27 @@ This is a combination of a (Java) ``UUID`` representing a globally unique 128 bi
|
|||||||
string which can be paired with it. For instance the string may represent an existing "weak" (not guaranteed unique)
|
string which can be paired with it. For instance the string may represent an existing "weak" (not guaranteed unique)
|
||||||
identifier for convenience purposes.
|
identifier for convenience purposes.
|
||||||
|
|
||||||
FungibleAssets and Cash
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
There is a common ``FungibleAsset`` superclass for contracts which model fungible assets, which also provides a standard
|
|
||||||
interface for its subclasses' state objects to implement. The clear use-case is ``Cash``, however ``FungibleAsset`` is
|
|
||||||
intended to be readily extensible to cover other assets, for example commodities could be modelled by using a subclass
|
|
||||||
whose state objects include further details (location of the commodity, origin, grade, etc.) as needed.
|
|
||||||
|
|
||||||
Transaction lifecycle types
|
Transaction lifecycle types
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
The ``WireTransaction`` class contains the core of a transaction without signatures, and with references to attachments
|
The ``WireTransaction`` class contains the core of a transaction without signatures, and with references to attachments
|
||||||
in place of the attachments themselves (see also :doc:`data-model`). Once signed these are encapsulated in the
|
in place of the attachments themselves (see also :doc:`key-concepts-data-model`). Once signed these are encapsulated in the
|
||||||
``SignedTransaction`` class. For processing a transaction (i.e. to verify it) it is first converted to a
|
``SignedTransaction`` class. For processing a transaction (i.e. to verify it) a ``SignedTransaction`` is then converted to a
|
||||||
``LedgerTransaction``, which involves verifying the signatures and associating them to the relevant command(s), and
|
``LedgerTransaction``, which involves verifying the signatures and associating them to the relevant command(s), and
|
||||||
resolving the attachment references to the attachments. Commands with valid signatures are encapsulated in the
|
resolving the attachment references to the attachments. Commands with valid signatures are encapsulated in the
|
||||||
``AuthenticatedObject`` type.
|
``AuthenticatedObject`` type.
|
||||||
|
|
||||||
.. note:: A ``LedgerTransaction`` has not necessarily had its contracts be run, and thus could be contract-invalid
|
.. note:: A ``LedgerTransaction`` has not necessarily had its contract code executed, and thus could be contract-invalid
|
||||||
(but not signature-invalid). You can use the ``verify`` method as shown below to run the contracts.
|
(but not signature-invalid). You can use the ``verify`` method as shown below to validate the contracts.
|
||||||
|
|
||||||
When constructing a new transaction from scratch, you use ``TransactionBuilder``, which is a mutable transaction that
|
When constructing a new transaction from scratch, you use ``TransactionBuilder``, which is a mutable transaction that
|
||||||
can be signed once modification of the internals is complete. It is typical for contract classes to expose helper
|
can be signed once its construction is complete. This builder class should be used to create the initial transaction representation
|
||||||
methods that can contribute to a ``TransactionBuilder``.
|
(before signature, before verification). It is intended to be passed around code that may edit it by adding new states/commands.
|
||||||
|
Then once the states and commands are right, this class can be used as a holding bucket to gather signatures from multiple parties.
|
||||||
|
It is typical for contract classes to expose helper methods that can contribute to a ``TransactionBuilder``. Once a transaction
|
||||||
|
has been constructed using the builders ``toWireTransaction`` or ``toSignedTransaction`` function, it shared with other
|
||||||
|
participants using the :doc:`key-concepts-flow-framework`.
|
||||||
|
|
||||||
Here's an example of building a transaction that creates an issuance of bananas (note that bananas are not a real
|
Here's an example of building a transaction that creates an issuance of bananas (note that bananas are not a real
|
||||||
contract type in the library):
|
contract type in the library):
|
||||||
@ -127,25 +81,22 @@ contract type in the library):
|
|||||||
In a unit test, you would typically use a freshly created ``MockServices`` object, or more realistically, you would
|
In a unit test, you would typically use a freshly created ``MockServices`` object, or more realistically, you would
|
||||||
write your tests using the :doc:`domain specific language for writing tests <tutorial-test-dsl>`.
|
write your tests using the :doc:`domain specific language for writing tests <tutorial-test-dsl>`.
|
||||||
|
|
||||||
Party and PublicKey
|
Party and CompositeKey
|
||||||
-------------------
|
----------------------
|
||||||
|
|
||||||
Entities using the network are called *parties*. Parties can sign structures using keys, and a party may have many
|
Entities using the network are called *parties*. Parties can sign structures using keys, and a party may have many
|
||||||
keys under their control.
|
keys under their control.
|
||||||
|
|
||||||
Parties may sometimes be identified pseudonymously, for example, in a transaction sent to your node as part of a
|
Parties may sometimes be identified pseudonymously. For example, in a transaction sent to your node as part of a
|
||||||
chain of custody it is important you can convince yourself of the transaction's validity, but equally important that
|
chain of custody it is important you can convince yourself of the transaction's validity, but equally important that
|
||||||
you don't learn anything about who was involved in that transaction. In these cases a public key may be present
|
you don't learn anything about who was involved in that transaction. In these cases a public key may be present
|
||||||
without any identifying information about who owns it.
|
without any identifying information about who owns it.
|
||||||
|
|
||||||
Identities of parties involved in signing a transaction can be represented simply by a ``PublicKey``, or by further
|
Identities of parties involved in signing a transaction can be represented simply by a ``CompositeKey``, or by further
|
||||||
information (such as name) using the ``Party`` class. An ``AuthenticatedObject`` represents an object (like a command)
|
information (such as name) using the ``Party`` class. An ``AuthenticatedObject`` represents an object (like a command)
|
||||||
that has been signed by a set of parties.
|
that has been signed by a set of parties.
|
||||||
|
|
||||||
.. note:: These types are provisional and will change significantly in future as the identity framework becomes more
|
.. note:: These types are provisional and will change significantly in future as the identity framework becomes more fleshed out.
|
||||||
fleshed out.
|
|
||||||
|
|
||||||
.. _composite-keys:
|
|
||||||
|
|
||||||
Multi-signature support
|
Multi-signature support
|
||||||
-----------------------
|
-----------------------
|
||||||
@ -153,6 +104,8 @@ Multi-signature support
|
|||||||
Corda supports scenarios where more than one key or party is required to authorise a state object transition, for example:
|
Corda supports scenarios where more than one key or party is required to authorise a state object transition, for example:
|
||||||
"Either the CEO or 3 out of 5 of his assistants need to provide signatures".
|
"Either the CEO or 3 out of 5 of his assistants need to provide signatures".
|
||||||
|
|
||||||
|
.. _composite-keys:
|
||||||
|
|
||||||
Composite Keys
|
Composite Keys
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -163,12 +116,14 @@ node specifies a *threshold* of how many child signatures it requires.
|
|||||||
An illustration of an *"either Alice and Bob, or Charlie"* composite key:
|
An illustration of an *"either Alice and Bob, or Charlie"* composite key:
|
||||||
|
|
||||||
.. image:: resources/composite-key.png
|
.. image:: resources/composite-key.png
|
||||||
|
:align: center
|
||||||
:width: 300px
|
:width: 300px
|
||||||
|
|
||||||
To allow further flexibility, each child node can have an associated custom *weight* (the default is 1). The *threshold*
|
To allow further flexibility, each child node can have an associated custom *weight* (the default is 1). The *threshold*
|
||||||
then specifies the minimum total weight of all children required. Our previous example can also be expressed as:
|
then specifies the minimum total weight of all children required. Our previous example can also be expressed as:
|
||||||
|
|
||||||
.. image:: resources/composite-key-2.png
|
.. image:: resources/composite-key-2.png
|
||||||
|
:align: center
|
||||||
:width: 300px
|
:width: 300px
|
||||||
|
|
||||||
Verification
|
Verification
|
||||||
@ -184,7 +139,7 @@ Signature verification is performed in two stages:
|
|||||||
Date support
|
Date support
|
||||||
------------
|
------------
|
||||||
|
|
||||||
There are a number of supporting interfaces and classes for use by contract which deal with dates (especially in the
|
There are a number of supporting interfaces and classes for use by contracts which deal with dates (especially in the
|
||||||
context of deadlines). As contract negotiation typically deals with deadlines in terms such as "overnight", "T+3",
|
context of deadlines). As contract negotiation typically deals with deadlines in terms such as "overnight", "T+3",
|
||||||
etc., it's desirable to allow conversion of these terms to their equivalent deadline. ``Tenor`` models the interval
|
etc., it's desirable to allow conversion of these terms to their equivalent deadline. ``Tenor`` models the interval
|
||||||
before a deadline, such as 3 days, etc., while ``DateRollConvention`` describes how deadlines are modified to take
|
before a deadline, such as 3 days, etc., while ``DateRollConvention`` describes how deadlines are modified to take
|
||||||
@ -196,11 +151,11 @@ bank holidays). The ``BusinessCalendar`` class models these calendars of busines
|
|||||||
from files on disk, but in future this is likely to involve reference data oracles in order to ensure consensus on the
|
from files on disk, but in future this is likely to involve reference data oracles in order to ensure consensus on the
|
||||||
dates used.
|
dates used.
|
||||||
|
|
||||||
Cryptography & maths support
|
Cryptography and maths support
|
||||||
----------------------------
|
------------------------------
|
||||||
|
|
||||||
The ``SecureHash`` class represents a secure hash of unknown algorithm. We currently define only a single subclass,
|
The ``SecureHash`` class represents a secure hash of unknown algorithm. We currently define only a single subclass,
|
||||||
``SecureHash.SHA256``. There are utility methods to create them, parse them and so on.
|
``SecureHash.SHA256``. There are utility methods to create them, parse them and so on.
|
||||||
|
|
||||||
We also provide some mathematical utilities, in particular a set of interpolators and classes for working with
|
We also provide some mathematical utilities, in particular a set of interpolators and classes for working with
|
||||||
splines. These can be found in the `maths package <api/kotlin/corda/net.corda.core.math/index.html>`_.
|
splines. These can be found in the `maths package <api/net.corda.core.math/index.html>`_.
|
142
docs/build/html/_sources/key-concepts-data-model.txt
vendored
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
Data model
|
||||||
|
==========
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
Corda uses the so-called "UTXO set" model (unspent transaction output). In this model, the database
|
||||||
|
does not track accounts or balances. An entry is either spent or not spent but it cannot be changed. In this model the
|
||||||
|
database is a set of immutable rows keyed by (hash:output index). Transactions define outputs that append new rows and
|
||||||
|
inputs which consume existing rows.
|
||||||
|
|
||||||
|
The Corda ledger is defined as a set of immutable **states**, which are created and destroyed by digitally signed **transactions**.
|
||||||
|
Each transaction points to a set of states that it will consume/destroy, these are called **inputs**, and contains a set
|
||||||
|
of new states that it will create, these are called **outputs**.
|
||||||
|
Although the ledger is shared, it is not always the case that transactions and ledger entries are globally visible.
|
||||||
|
In cases where a set of transactions stays within a small subgroup of users it is possible to keep the relevant
|
||||||
|
data purely within that group. To ensure consistency, we rely heavily on secure hashes like SHA-256 to identify things.
|
||||||
|
|
||||||
|
The Corda model provides the following additional features:
|
||||||
|
|
||||||
|
* There is no global broadcast at any point.
|
||||||
|
* States can include arbitrary typed data.
|
||||||
|
* Transactions invoke not only input contracts but also the contracts of the outputs.
|
||||||
|
* Contracts refer to a bundle of business logic that may handle various different tasks, beyond transaction verification.
|
||||||
|
* Contracts are Turing-complete and can be written in any ordinary programming language that targets the JVM.
|
||||||
|
* Arbitrarily-precise time-bounds may be specified in transactions (which must be attested to by a notary)
|
||||||
|
* Primary consensus implementations use block-free conflict resolution algorithms.
|
||||||
|
* Transactions are not ordered using a block chain and by implication Corda does not use miners or proof-of-work.
|
||||||
|
Instead each state points to a notary, which is a service that guarantees it will sign a transaction only if all the
|
||||||
|
input states are un-consumed.
|
||||||
|
|
||||||
|
Corda provides three main tools to achieve global distributed consensus:
|
||||||
|
|
||||||
|
* Smart contract logic to ensure state transitions are valid according to the pre-agreed rules.
|
||||||
|
* Uniqueness and timestamping services to order transactions temporally and eliminate conflicts.
|
||||||
|
* An :doc:`orchestration framework <key-concepts-flow-framework>` which simplifies the process of writing complex multi-step protocols between multiple different parties.
|
||||||
|
|
||||||
|
Comparisons of the Corda data model with Bitcoin and Ethereum can be found in the white papers.
|
||||||
|
|
||||||
|
States
|
||||||
|
------
|
||||||
|
A state object represents an agreement between two or more parties, the evolution of which governed by machine-readable contract code.
|
||||||
|
This code references, and is intended to implement, portions of human-readable legal prose.
|
||||||
|
It is intended to be shared only with those who have a legitimate reason to see it.
|
||||||
|
|
||||||
|
The following diagram illustrates a state object:
|
||||||
|
|
||||||
|
.. image:: resources/contract.png
|
||||||
|
|
||||||
|
In the diagram above, we see a state object representing a cash claim of £100 against a commercial bank, owned by a fictional shipping company.
|
||||||
|
|
||||||
|
.. note:: Legal prose (depicted above in grey-shade) is currently implemented as an unparsed reference to the natural language
|
||||||
|
contract that the code is supposed to express (usually a hash of the contract's contents).
|
||||||
|
|
||||||
|
States contain arbitrary data, but they always contain at minimum a hash of the bytecode of a
|
||||||
|
**contract code** file, which is a program expressed in JVM byte code that runs sandboxed inside a Java virtual machine.
|
||||||
|
Contract code (or just "contracts" in the rest of this document) are globally shared pieces of business logic.
|
||||||
|
|
||||||
|
.. note:: In the current code dynamic loading of contracts is not implemented. This will change in the near future.
|
||||||
|
|
||||||
|
Contracts
|
||||||
|
---------
|
||||||
|
Contracts define part of the business logic of the ledger.
|
||||||
|
|
||||||
|
Corda enforces business logic through smart contract code, which is constructed as a pure function (called "verify") that either accepts
|
||||||
|
or rejects a transaction, and which can be composed from simpler, reusable functions. The functions interpret transactions
|
||||||
|
as taking states as inputs and producing output states through the application of (smart contract) commands, and accept
|
||||||
|
the transaction if the proposed actions are valid. Given the same transaction, a contract’s “verify” function always yields
|
||||||
|
exactly the same result. Contracts do not have storage or the ability to interact with anything.
|
||||||
|
|
||||||
|
.. note:: In the future, contracts will be mobile. Nodes will download and run contracts inside a sandbox without any review in some deployments,
|
||||||
|
although we envisage the use of signed code for Corda deployments in the regulated sphere. Corda will use an augmented
|
||||||
|
JVM custom sandbox that is radically more restrictive than the ordinary JVM sandbox, and it will enforce not only
|
||||||
|
security requirements but also deterministic execution.
|
||||||
|
|
||||||
|
To further aid writing contracts we introduce the concept of :doc:`clauses` which provide a means of re-using common
|
||||||
|
verification logic.
|
||||||
|
|
||||||
|
Transactions
|
||||||
|
------------
|
||||||
|
Transaction are used to update the ledger by consuming existing state objects and producing new state objects.
|
||||||
|
|
||||||
|
A transaction update is accepted according to the following two aspects of consensus:
|
||||||
|
|
||||||
|
#. Transaction validity: parties can ensure that the proposed transaction and all its ancestors are valid
|
||||||
|
by checking that the associated contract code runs successfully and has all the required signatures
|
||||||
|
#. Transaction uniqueness: parties can ensure there exists no other transaction, over which we have previously reached
|
||||||
|
consensus (validity and uniqueness), that consumes any of the same states. This is the responsibility of a notary service.
|
||||||
|
|
||||||
|
Beyond inputs and outputs, transactions may also contain **commands**, small data packets that
|
||||||
|
the platform does not interpret itself but which parameterise execution of the contracts. They can be thought of as
|
||||||
|
arguments to the verify function. Each command has a list of **composite keys** associated with it. The platform ensures
|
||||||
|
that the transaction has signatures matching every key listed in the commands before the contracts start to execute. Thus, a verify
|
||||||
|
function can trust that all listed keys have signed the transaction, but is responsible for verifying that any keys required
|
||||||
|
for the transaction to be valid from the verify function's perspective are included in the list. Public keys
|
||||||
|
may be random/identityless for privacy, or linked to a well known legal identity, for example via a
|
||||||
|
*public key infrastructure* (PKI).
|
||||||
|
|
||||||
|
.. note:: Linkage of keys with identities via a PKI is only partially implemented in the current code.
|
||||||
|
|
||||||
|
Commands are always embedded inside a transaction. Sometimes, there's a larger piece of data that can be reused across
|
||||||
|
many different transactions. For this use case, we have **attachments**. Every transaction can refer to zero or more
|
||||||
|
attachments by hash. Attachments are always ZIP/JAR files, which may contain arbitrary content. These files are
|
||||||
|
then exposed on the classpath and so can be opened by contract code in the same manner as any JAR resources
|
||||||
|
would be loaded.
|
||||||
|
|
||||||
|
Note that there is nothing that explicitly binds together specific inputs, outputs, commands or attachments. Instead,
|
||||||
|
it's up to the contract code to interpret the pieces inside the transaction and ensure they fit together correctly. This
|
||||||
|
is done to maximise flexibility for the contract developer.
|
||||||
|
|
||||||
|
Transactions may sometimes need to provide a contract with data from the outside world. Examples may include stock
|
||||||
|
prices, facts about events or the statuses of legal entities (e.g. bankruptcy), and so on. The providers of such
|
||||||
|
facts are called **oracles** and they provide facts to the ledger by signing transactions that contain commands they
|
||||||
|
recognise, or by creating signed attachments. The commands contain the fact and the signature shows agreement to that fact.
|
||||||
|
|
||||||
|
Time is also modelled as a fact and represented as a **timestamping command** placed inside the transaction. This specifies a
|
||||||
|
time window in which the transaction is considered valid for notarisation. The time window can be open ended (i.e. with a start but no end or vice versa).
|
||||||
|
In this way transactions can be linked to the notary's clock.
|
||||||
|
|
||||||
|
It is possible for a single Corda network to have multiple competing notaries. A new (output) state is tied to a specific
|
||||||
|
notary when it is created. Transactions can only consume (input) states that are all associated with the same notary.
|
||||||
|
A special type of transaction is provided that can move a state (or set of states) from one notary to another.
|
||||||
|
|
||||||
|
.. note:: Currently the platform code will not automatically re-assign states to a single notary. This is a future planned feature.
|
||||||
|
|
||||||
|
Transaction Validation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
When a transaction is presented to a node as part of a flow it may need to be checked. Checking original transaction validity is
|
||||||
|
the responsibility of the ``ResolveTransactions`` flow. This flow performs a breadth-first search over the transaction graph,
|
||||||
|
downloading any missing transactions into local storage and validating them. The search bottoms out at transactions without inputs
|
||||||
|
(eg. these are mostly created from issuance transactions). A transaction is not considered valid if any of its transitive dependencies are invalid.
|
||||||
|
|
||||||
|
.. note:: Non-validating notaries assume transaction validity and do not request transaction data or their dependencies
|
||||||
|
beyond the list of states consumed.
|
||||||
|
|
||||||
|
The tutorial " :doc:`tutorial-contract` "provides a hand-ons walk-through using these concepts.
|
||||||
|
|
||||||
|
Transaction Representation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
By default, all transaction data (input and output states, commands, attachments) is visible to all participants in
|
||||||
|
a multi-party, multi-flow business workflow. :doc:`merkle-trees` describes how Corda uses Merkle trees to
|
||||||
|
ensure data integrity and hiding of sensitive data within a transaction that shouldn't be visible in its entirety to all
|
||||||
|
participants (eg. oracles nodes providing facts).
|
47
docs/build/html/_sources/key-concepts-ecosystem.txt
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
Corda ecosystem
|
||||||
|
===============
|
||||||
|
|
||||||
|
A Corda network consists of the following components:
|
||||||
|
|
||||||
|
* Nodes, where each node represents a JVM run-time environment hosting Corda services and executing applications ("CorDapps").
|
||||||
|
Nodes communicate using AMQP/1.0 over TLS.
|
||||||
|
* A permissioning service that automates the process of provisioning TLS certificates.
|
||||||
|
* A network map service that publishes information about nodes on the network.
|
||||||
|
* One or more pluggable notary service types (which may be distributed over multiple nodes).
|
||||||
|
A notary guarantees uniqueness and validity of transactions.
|
||||||
|
* Zero or more oracle services. An oracle is a well known service that signs transactions if they state a fact and that fact is considered to be true.
|
||||||
|
* CorDapps which represent participant applications that execute contract code and communicate using the flow framework to achieve consensus over some business activity
|
||||||
|
* Standalone Corda applications that provide manageability and tooling support to a Corda network.
|
||||||
|
|
||||||
|
These components are illustrated in the following diagram:
|
||||||
|
|
||||||
|
.. image:: resources/cordaNetwork.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
* Corda infrastructure services are those which all participants depend upon, such as the network map and notaries.
|
||||||
|
* Corda services can be deployed by participants, third parties or a central network operator (eg. such as R3);
|
||||||
|
this diagram is not intended to imply only a centralised model is supported
|
||||||
|
|
||||||
|
It is important to highlight the following:
|
||||||
|
|
||||||
|
* Corda is designed for semi-private networks in which admission requires obtaining an identity signed by a root authority.
|
||||||
|
* Nodes are arranged in an authenticated peer to peer network. All communication is direct.
|
||||||
|
* Data is shared on a need-to-know basis. Nodes provide the dependency graph of a transaction they are sending to another node on demand, but there is no global broadcast of all transactions.
|
||||||
|
* Nodes are backed by a relational database and data placed in the ledger can be queried using SQL
|
||||||
|
* The network map publishes the IP addresses through which every node on the network can be reached, along with the identity certificates of those nodes and the services they provide.
|
||||||
|
* All communication takes the form of small multi-party sub-protocols called flows.
|
||||||
|
* Oracles represent gateways to proprietary (or other) business logic executors (e.g., central counterparties or valuation agents) that can be verified on-ledger by participants.
|
||||||
|
|
||||||
|
CorDapps
|
||||||
|
--------
|
||||||
|
Corda is a platform for the writing of “CorDapps”: applications that extend the distributed ledger with new capabilities.
|
||||||
|
Such apps define new data types, new inter-node protocol flows and the “smart contracts” that determine allowed changes.
|
||||||
|
The combination of state objects (data), contract code (allowable operations), transaction flows (business logic
|
||||||
|
choreography), any necessary APIs, vault plugins, and UI components can be thought of as a shared ledger application,
|
||||||
|
or corda distributed application (“CorDapp”). This is the core set of components a contract developer on the platform
|
||||||
|
should expect to build.
|
||||||
|
|
||||||
|
Examples of CorDapps include asset trading (see :ref:`irs-demo` and :ref:`trader-demo`), portfolio valuations (see :ref:`simm-demo`), trade finance,
|
||||||
|
post-trade order matching, KYC/AML, etc.
|
71
docs/build/html/_sources/key-concepts-financial-model.txt
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
Financial model
|
||||||
|
===============
|
||||||
|
|
||||||
|
Corda provides a large standard library of data types used in financial applications and contract state objects.
|
||||||
|
These provide a common language for states and contracts.
|
||||||
|
|
||||||
|
Amount
|
||||||
|
------
|
||||||
|
|
||||||
|
The `Amount <api/net.corda.core.contracts/-amount/index.html>`_ class is used to represent an amount of some
|
||||||
|
fungible asset. It is a generic class which wraps around a type used to define the underlying product, called
|
||||||
|
the *token*. For instance it can be the standard JDK type ``Currency``, or an ``Issued`` instance, or this can be
|
||||||
|
a more complex type such as an obligation contract issuance definition (which in turn contains a token definition
|
||||||
|
for whatever the obligation is to be settled in).
|
||||||
|
|
||||||
|
.. note:: Fungible is used here to mean that instances of an asset is interchangeable for any other identical instance,
|
||||||
|
and that they can be split/merged. For example a £5 note can reasonably be exchanged for any other £5 note, and
|
||||||
|
a £10 note can be exchanged for two £5 notes, or vice-versa.
|
||||||
|
|
||||||
|
Here are some examples:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
// A quantity of some specific currency like pounds, euros, dollars etc.
|
||||||
|
Amount<Currency>
|
||||||
|
// A quantity of currency that is issued by a specific issuer, for instance central bank vs other bank dollars
|
||||||
|
Amount<Issued<Currency>>
|
||||||
|
// A quantity of a product governed by specific obligation terms
|
||||||
|
Amount<Obligation.Terms<P>>
|
||||||
|
|
||||||
|
``Amount`` represents quantities as integers. For currencies the quantity represents pennies, cents or whatever
|
||||||
|
else the smallest integer amount for that currency is. You cannot use ``Amount`` to represent negative quantities
|
||||||
|
or fractional quantities: if you wish to do this then you must use a different type e.g. ``BigDecimal``. ``Amount``
|
||||||
|
defines methods to do addition and subtraction and these methods verify that the tokens on both sides of the operator
|
||||||
|
are equal (these are operator overloads in Kotlin and can be used as regular methods from Java). There are also
|
||||||
|
methods to do multiplication and division by integer amounts.
|
||||||
|
|
||||||
|
``Issued`` refers to a product (which can be cash, a cash-like thing, assets, or generally anything else that's
|
||||||
|
quantifiable with integer quantities) and an associated ``PartyAndReference`` that describes the issuer of that contract.
|
||||||
|
An issued product typically follows a lifecycle which includes issuance, movement and exiting from the ledger (for example,
|
||||||
|
see the ``Cash`` contract and its associated *state* and *commands*)
|
||||||
|
|
||||||
|
Financial states
|
||||||
|
----------------
|
||||||
|
In additional to the common state types, a number of interfaces extend ``ContractState`` to model financial state such as:
|
||||||
|
|
||||||
|
``LinearState``
|
||||||
|
A state which has a unique identifier beyond its StateRef and carries it through state transitions.
|
||||||
|
Such a state cannot be duplicated, merged or split in a transaction: only continued or deleted. A linear state is
|
||||||
|
useful when modelling an indivisible/non-fungible thing like a specific deal, or an asset that can't be
|
||||||
|
split (like a rare piece of art).
|
||||||
|
|
||||||
|
``DealState``
|
||||||
|
A LinearState representing an agreement between two or more parties. Intended to simplify implementing generic
|
||||||
|
protocols that manipulate many agreement types.
|
||||||
|
|
||||||
|
``FungibleAsset``
|
||||||
|
A FungibleAsset is intended to be used for contract states representing assets which are fungible, countable and issued by a
|
||||||
|
specific party. States contain assets which are equivalent (such as cash of the same currency), so records of their existence
|
||||||
|
can be merged or split as needed where the issuer is the same. For instance, dollars issued by the Fed are fungible and
|
||||||
|
countable (in cents), barrels of West Texas crude are fungible and countable (oil from two small containers can be poured into one large
|
||||||
|
container), shares of the same class in a specific company are fungible and countable, and so on.
|
||||||
|
|
||||||
|
The following diagram illustrates the complete Contract State hierarchy:
|
||||||
|
|
||||||
|
.. image:: resources/financialContractStateModel.png
|
||||||
|
|
||||||
|
Note there are currently two packages, a core library and a finance model specific library.
|
||||||
|
Developers may re-use or extend the Finance types directly or write their own by extending the base types from the Core library.
|
37
docs/build/html/_sources/key-concepts-flow-framework.txt
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
Flow framework
|
||||||
|
--------------
|
||||||
|
In Corda all communication takes the form of structured sequences of messages passed between parties which we call flows.
|
||||||
|
|
||||||
|
Flows enable complex multi-step, multi-party business interactions to be modelled as blocking code without a central controller.
|
||||||
|
The code is transformed into an asynchronous state machine, with checkpoints written to the node’s backing database when messages are sent and received.
|
||||||
|
A node may potentially have millions of flows active at once and they may last days, across node restarts and even upgrades.
|
||||||
|
|
||||||
|
A flow library is provided to enable developers to re-use common flow types such as notarisation, membership broadcast,
|
||||||
|
transaction resolution and recording, and so on.
|
||||||
|
|
||||||
|
APIs are provided to send and receive object graphs to and from other identities on the network, embed sub-flows,
|
||||||
|
report progress information to observers and even interact with people (for manual resolution of exceptional scenarios)
|
||||||
|
|
||||||
|
Flows are embedded within CorDapps and deployed to a participant's node for execution.
|
||||||
|
|
||||||
|
.. note:: We will be implementing the concept of a flow hospital to provide a means for a node administrator to decide
|
||||||
|
whether a paused flow should be killed or repaired. Flows enter this state if they throw exceptions or explicitly request human assistance.
|
||||||
|
|
||||||
|
Section 4 of the `Technical white paper`_ provides further detail of the above features.
|
||||||
|
|
||||||
|
The following diagram illustrates a sample multi-party business flow:
|
||||||
|
|
||||||
|
.. image:: resources/flowFramework.png
|
||||||
|
|
||||||
|
Note the following:
|
||||||
|
|
||||||
|
* there are 3 participants in this workflow including the notary
|
||||||
|
* the Buyer and Seller flows (depicted in green) are custom written by developers and deployed within a CorDapp
|
||||||
|
* the custom written flows invoke both financial library flows such as ``TwoPartyTradeFlow`` (depicted in orange) and core
|
||||||
|
library flows such as ``ResolveTransactionsFlow`` and ``NotaryFlow`` (depicted in yellow)
|
||||||
|
* each side of the flow illustrates the stage of execution with a progress tracker notification
|
||||||
|
* activities within a flow directly or indirectly interact with its node's ledger (eg. to record a signed, notarised transaction) and vault (eg. to perform a spend of some fungible asset)
|
||||||
|
* flows interact across parties using send, receive and sendReceive messaging semantics (by implementing the ``FlowLogic`` interface)
|
||||||
|
|
||||||
|
.. _`Technical white paper`: _static/corda-technical-whitepaper.pdf
|
46
docs/build/html/_sources/key-concepts-security-model.txt
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
Security model
|
||||||
|
==============
|
||||||
|
|
||||||
|
Corda has been designed from the ground up to implement a global, decentralised database where all nodes are assumed to be
|
||||||
|
untrustworthy. This means that each node must actively cross-check each other's work to reach consensus
|
||||||
|
amongst a group of interacting participants.
|
||||||
|
|
||||||
|
The security model plays a role in the following areas:
|
||||||
|
|
||||||
|
* Identity:
|
||||||
|
Corda is designed for semi-private networks in which admission requires obtaining an identity signed by a root authority.
|
||||||
|
This assumption is pervasive – the flow API provides messaging in terms of identities, with routing and delivery to underlying nodes being handled automatically.
|
||||||
|
See sections 3.2 of the `Technical white paper`_ for further details on identity and the permissioning service.
|
||||||
|
|
||||||
|
* Notarisation: pluggable notaries and algorithms offering different levels of trust.
|
||||||
|
Notaries may be validating or non-validating. A validating notary will resolve and fully check transactions they are asked to deconflict.
|
||||||
|
Without the use of any other privacy features, they gain full visibility into every transaction.
|
||||||
|
On the other hand, non-validating notaries assume transaction validity and do not request transaction data or their dependencies
|
||||||
|
beyond the list of states consumed (and thus, their level of trust is much lower and exposed to malicious use of transaction inputs).
|
||||||
|
From an algorithm perspective, Corda currently provides a distributed notary implementation that uses Raft.
|
||||||
|
|
||||||
|
.. note:: Future notary algorithms may include BFT and hardware assisted non-BFT algorithms (where non-BFT algorithms
|
||||||
|
are converted into a more trusted form using remote attestation and hardware protection).
|
||||||
|
|
||||||
|
* Authentication, authorisation and entitlements:
|
||||||
|
Network permissioning, including node to node authentication, is performed using TLS and certificates.
|
||||||
|
See :doc:`permissioning` for further detail.
|
||||||
|
|
||||||
|
.. warning:: API level authentication (RPC, Web) is currently simple username/password for demonstration purposes and will be revised.
|
||||||
|
Similarly, authorisation is currently based on permission groups applied to flow execution.
|
||||||
|
This is subject to design review with views to selecting a proven, mature entitlements solution.
|
||||||
|
|
||||||
|
Privacy techniques
|
||||||
|
|
||||||
|
* Partial data visibility: transactions are not globally broadcast as in many other systems.
|
||||||
|
* Transaction tear-offs: Transactions are structured as Merkle trees, and may have individual subcomponents be revealed to parties who already know the Merkle root hash. Additionally, they may sign the transaction without being able to see all of it.
|
||||||
|
|
||||||
|
See :doc:`merkle-trees` for further detail.
|
||||||
|
|
||||||
|
* Multi-signature support: Corda uses composite keys to support scenarios where more than one key or party is required to authorise a state object transition.
|
||||||
|
|
||||||
|
.. note:: Future privacy techniques will include key randomisation, graph pruning, deterministic JVM sandboxing and support for secure signing devices.
|
||||||
|
See sections 10 and 13 of the `Technical white paper`_ for detailed descriptions of these techniques and features.
|
||||||
|
|
||||||
|
.. _`Technical white paper`: _static/corda-technical-whitepaper.pdf
|
||||||
|
|