Merge remote-tracking branch 'corda-public/master'

This commit is contained in:
Chris Rankin 2017-02-10 14:23:46 +00:00
commit f180ae96bd
1626 changed files with 17905 additions and 15310 deletions

2
.gitignore vendored
View File

@ -11,7 +11,6 @@ tags
.gradle
local.properties
/docs/build/doctrees
# General build files
**/build/*
@ -34,7 +33,6 @@ lib/dokka.jar
.idea/libraries
.idea/shelf
.idea/dataSources
.idea/modules.xml
# if you remove the above rule, at least ignore the following:

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.notarydemo.NotaryDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="--certificates=&quot;build/notary-demo-nodes/Party/certificates&quot;" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -4,7 +4,7 @@ buildscript {
file("publish.properties").withInputStream { props.load(it) }
// Our version: bump this on release.
ext.corda_version = "0.8-SNAPSHOT"
ext.corda_version = "0.9-SNAPSHOT"
ext.gradle_plugins_version = props.getProperty("gradlePluginsVersion")
// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.

View File

@ -1,17 +0,0 @@
#!/usr/bin/env bash
# Creates three nodes. A network map and notary node and two regular nodes that can be extended with cordapps.
set -euo pipefail
trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
export CAPSULE_CACHE_DIR=cache
pushd nameserver
( java -jar JAR_NAME )&
popd
pushd nodea
( java -jar JAR_NAME )&
popd
pushd nodeb
( java -jar JAR_NAME )&
popd
read -p 'Any key to exit'
kill $(jobs -p)

View File

@ -8,8 +8,11 @@ import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.random63BitValue
import net.corda.core.serialization.OpaqueBytes
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.DriverBasedTest
import net.corda.node.driver.NodeHandle
import net.corda.node.driver.driver
import net.corda.node.internal.Node
import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
@ -23,7 +26,10 @@ import org.junit.Before
import org.junit.Test
class CordaRPCClientTest : NodeBasedTest() {
private val rpcUser = User("user1", "test", permissions = setOf(startFlowPermission<CashFlow>()))
private val rpcUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>()
))
private lateinit var node: Node
private lateinit var client: CordaRPCClient
@ -60,8 +66,8 @@ class CordaRPCClientTest : NodeBasedTest() {
val proxy = client.proxy()
println("Starting flow")
val flowHandle = proxy.startFlow(
::CashFlow,
CashCommand.IssueCash(20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity, node.info.legalIdentity))
::CashIssueFlow,
20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity, node.info.legalIdentity)
println("Started flow, waiting on result")
flowHandle.progress.subscribe {
println("PROGRESS $it")
@ -73,10 +79,10 @@ class CordaRPCClientTest : NodeBasedTest() {
fun `FlowException thrown by flow`() {
client.start(rpcUser.username, rpcUser.password)
val proxy = client.proxy()
val handle = proxy.startFlow(::CashFlow, CashCommand.PayCash(
amount = 100.DOLLARS.issuedBy(node.info.legalIdentity.ref(1)),
recipient = node.info.legalIdentity
))
val handle = proxy.startFlow(::CashPaymentFlow,
100.DOLLARS.issuedBy(node.info.legalIdentity.ref(1)),
node.info.legalIdentity
)
// TODO Restrict this to CashException once RPC serialisation has been fixed
assertThatExceptionOfType(FlowException::class.java).isThrownBy {
handle.returnValue.getOrThrow()

View File

@ -19,8 +19,9 @@ import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashCommand
import net.corda.flows.CashFlow
import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.DriverBasedTest
import net.corda.node.driver.driver
import net.corda.node.services.User
@ -49,7 +50,11 @@ class NodeMonitorModelTest : DriverBasedTest() {
lateinit var newNode: (String) -> NodeInfo
override fun setup() = driver {
val cashUser = User("user1", "test", permissions = setOf(startFlowPermission<CashFlow>()))
val cashUser = User("user1", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>(),
startFlowPermission<CashExitFlow>())
)
val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser))
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
@ -94,12 +99,12 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue works end to end`() {
rpc.startFlow(::CashFlow, CashCommand.IssueCash(
amount = Amount(100, USD),
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
recipient = aliceNode.legalIdentity,
notary = notaryNode.notaryIdentity
))
rpc.startFlow(::CashIssueFlow,
Amount(100, USD),
OpaqueBytes(ByteArray(1, { 1 })),
aliceNode.legalIdentity,
notaryNode.notaryIdentity
)
vaultUpdates.expectEvents(isStrict = false) {
sequence(
@ -119,17 +124,17 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue and move`() {
rpc.startFlow(::CashFlow, CashCommand.IssueCash(
amount = Amount(100, USD),
issueRef = OpaqueBytes(ByteArray(1, { 1 })),
recipient = aliceNode.legalIdentity,
notary = notaryNode.notaryIdentity
)).returnValue.getOrThrow()
rpc.startFlow(::CashIssueFlow,
Amount(100, USD),
OpaqueBytes(ByteArray(1, { 1 })),
aliceNode.legalIdentity,
notaryNode.notaryIdentity
).returnValue.getOrThrow()
rpc.startFlow(::CashFlow, CashCommand.PayCash(
amount = Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
recipient = aliceNode.legalIdentity
))
rpc.startFlow(::CashPaymentFlow,
Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
aliceNode.legalIdentity
)
var issueSmId: StateMachineRunId? = null
var moveSmId: StateMachineRunId? = null

View File

@ -5,7 +5,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.Party
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.TransactionBuilder
import net.corda.flows.CashCommand
import net.corda.flows.CashFlowCommand
import java.util.*
/**
@ -64,7 +64,7 @@ class EventGenerator(
val issueCashGenerator =
amountGenerator.combine(partyGenerator, issueRefGenerator) { amount, to, issueRef ->
CashCommand.IssueCash(
CashFlowCommand.IssueCash(
amount,
issueRef,
to,
@ -76,7 +76,7 @@ class EventGenerator(
amountIssuedGenerator.combine(
partyGenerator
) { amountIssued, recipient ->
CashCommand.PayCash(
CashFlowCommand.PayCash(
amount = amountIssued,
recipient = recipient
)
@ -84,7 +84,7 @@ class EventGenerator(
val exitCashGenerator =
amountIssuedGenerator.map {
CashCommand.ExitCash(
CashFlowCommand.ExitCash(
it.withoutIssuer(),
it.token.issuer.reference
)

View File

@ -10,7 +10,6 @@ import net.corda.core.transactions.TransactionBuilder
val DUMMY_PROGRAM_ID = DummyContract()
data class DummyContract(override val legalContractReference: SecureHash = SecureHash.sha256("")) : Contract {
interface State : ContractState {
val magicNumber: Int
}
@ -31,8 +30,7 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
data class MultiOwnerState(override val magicNumber: Int = 0,
val owners: List<CompositeKey>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<CompositeKey>
get() = owners
override val participants: List<CompositeKey> get() = owners
}
interface Commands : CommandData {
@ -46,14 +44,20 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
companion object {
@JvmStatic
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
val state = SingleOwnerState(magicNumber, owner.party.owningKey)
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey))
fun generateInitial(magicNumber: Int, notary: Party, owner: PartyAndReference, vararg otherOwners: PartyAndReference): TransactionBuilder {
val owners = listOf(owner) + otherOwners
return if (owners.size == 1) {
val state = SingleOwnerState(magicNumber, owners.first().party.owningKey)
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey))
} else {
val state = MultiOwnerState(magicNumber, owners.map { it.party.owningKey })
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey }))
}
}
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: CompositeKey) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: CompositeKey): TransactionBuilder {
require(priors.size > 0)
require(priors.isNotEmpty())
val priorState = priors[0].state.data
val (cmd, state) = priorState.withNewOwner(newOwner)
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(

View File

@ -0,0 +1,59 @@
package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.WireTransaction
import net.corda.flows.ContractUpgradeFlow
// The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2()
/**
* Dummy contract state for testing of the upgrade process.
*/
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
override val legacyContract = DUMMY_PROGRAM_ID
data class State(val magicNumber: Int = 0, val owners: List<CompositeKey>) : ContractState {
override val contract = DUMMY_V2_PROGRAM_ID
override val participants: List<CompositeKey> = owners
}
interface Commands : CommandData {
class Create : TypeOnlyCommandData(), Commands
class Move : TypeOnlyCommandData(), Commands
}
override fun upgrade(state: DummyContract.State): DummyContractV2.State {
return DummyContractV2.State(state.magicNumber, state.participants)
}
override fun verify(tx: TransactionForContract) {
if (tx.commands.any { it.value is UpgradeCommand }) ContractUpgradeFlow.verify(tx)
// Other verifications.
}
// The "empty contract"
override val legalContractReference: SecureHash = SecureHash.sha256("")
/**
* Generate an upgrade transaction from [DummyContract].
*
* Note: This is a convenience helper method used for testing only.
*
* @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid.
*/
fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<CompositeKey>> {
val notary = states.map { it.state.notary }.single()
require(states.isNotEmpty())
val signees = states.flatMap { it.state.data.participants }.toSet()
return Pair(TransactionType.General.Builder(notary).apply {
states.forEach {
addInputState(it)
addOutputState(upgrade(it.state.data))
addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.toList())
}
}.toWireTransaction(), signees)
}
}

View File

@ -1,6 +1,7 @@
package net.corda.core.contracts
import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
@ -349,8 +350,8 @@ inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filt
* Reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal
* ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party.
*/
data class PartyAndReference(val party: Party, val reference: OpaqueBytes) {
override fun toString() = "${party.name}$reference"
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
override fun toString() = "${party}$reference"
}
/** Marker interface for classes that represent commands */
@ -380,7 +381,7 @@ interface IssueCommand : CommandData {
val nonce: Long
}
/** A common move command for contracts which can change owner. */
/** A common move command for contract states which can change owner. */
interface MoveCommand : CommandData {
/**
* Contract code the moved state(s) are for the attention of, for example to indicate that the states are moved in
@ -396,6 +397,9 @@ interface NetCommand : CommandData {
val type: NetType
}
/** Indicates that this transaction replaces the inputs contract state to another contract state */
data class UpgradeCommand(val upgradedContractClass: Class<UpgradedContract<*, *>>) : CommandData
/** Wraps an object that was signed by a public key, which may be a well known/recognised institutional key. */
data class AuthenticatedObject<out T : Any>(
val signers: List<CompositeKey>,
@ -444,6 +448,24 @@ interface Contract {
val legalContractReference: SecureHash
}
/**
* Interface which can upgrade state objects issued by a contract to a new state object issued by a different contract.
*
* @param OldState the old contract state (can be [ContractState] or other common supertype if this supports upgrading
* more than one state).
* @param NewState the upgraded contract state.
*/
interface UpgradedContract<in OldState : ContractState, out NewState : ContractState> : Contract {
val legacyContract: Contract
/**
* Upgrade contract's state object to a new state object.
*
* @throws IllegalArgumentException if the given state object is not one that can be upgraded. This can be either
* that the class is incompatible, or that the data inside the state object cannot be upgraded for some reason.
*/
fun upgrade(state: OldState): NewState
}
/**
* An attachment is a ZIP (or an optionally signed JAR) that contains one or more files. Attachments are meant to
* contain public static data which can be referenced from transactions and utilised from contracts. Good examples
@ -479,5 +501,3 @@ interface Attachment : NamedByHash {
throw FileNotFoundException()
}
}

View File

@ -17,6 +17,7 @@ sealed class TransactionType {
*
* Note: Presence of _signatures_ is not checked, only the public keys to be signed for.
*/
@Throws(TransactionVerificationException::class)
fun verify(tx: LedgerTransaction) {
require(tx.notary != null || tx.timestamp == null) { "Transactions with timestamps must be notarised." }
val duplicates = detectDuplicateInputs(tx)

View File

@ -3,6 +3,7 @@ package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.transactions.LedgerTransaction
import java.util.*
@ -85,30 +86,35 @@ data class TransactionForContract(val inputs: List<ContractState>,
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
}
class TransactionResolutionException(val hash: SecureHash) : Exception() {
override fun toString() = "Transaction resolution failure for $hash"
class TransactionResolutionException(val hash: SecureHash) : FlowException() {
override fun toString(): String = "Transaction resolution failure for $hash"
}
class AttachmentResolutionException(val hash : SecureHash) : FlowException() {
override fun toString(): String = "Attachment resolution failure for $hash"
}
class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTransaction, val tx2: LedgerTransaction) : Exception()
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : Exception(cause) {
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) {
class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause)
class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
class SignersMissing(tx: LedgerTransaction, val missing: List<CompositeKey>) : TransactionVerificationException(tx, null) {
override fun toString() = "Signers missing: ${missing.joinToString()}"
override fun toString(): String = "Signers missing: ${missing.joinToString()}"
}
class DuplicateInputStates(tx: LedgerTransaction, val duplicates: Set<StateRef>) : TransactionVerificationException(tx, null) {
override fun toString() = "Duplicate inputs: ${duplicates.joinToString()}"
override fun toString(): String = "Duplicate inputs: ${duplicates.joinToString()}"
}
class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {
override fun toString(): String = "Found unexpected notary change in transaction. Tx notary: ${tx.notary}, found: ${outputNotary}"
override fun toString(): String {
return "Found unexpected notary change in transaction. Tx notary: ${tx.notary}, found: $outputNotary"
}
}
class TransactionMissingEncumbranceException(tx: LedgerTransaction, val missing: Int, val inOut: Direction) : TransactionVerificationException(tx, null) {
override val message: String?
get() = "Missing required encumbrance ${missing} in ${inOut}"
override val message: String get() = "Missing required encumbrance $missing in $inOut"
}
enum class Direction {
INPUT,

View File

@ -0,0 +1,24 @@
package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey
/**
* The [AnonymousParty] class contains enough information to uniquely identify a [Party] while excluding private
* information such as name. It is intended to represent a party on the distributed ledger.
*/
open class AnonymousParty(val owningKey: CompositeKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(owningKey: PublicKey) : this(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 AnonymousParty && this.owningKey == other.owningKey
override fun hashCode(): Int = owningKey.hashCode()
// Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party]
// can put in the key and actual name
override fun toString() = "${owningKey.toBase58String()} <Anonymous>"
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes))
}

View File

@ -0,0 +1,66 @@
package net.corda.core.crypto
import java.util.*
/**
* Creation and verification of a Merkle Tree for a Wire Transaction.
*
* See: https://en.wikipedia.org/wiki/Merkle_tree
*
* Transaction is split into following blocks: inputs, attachments' refs, outputs, commands, notary,
* signers, tx type, timestamp. Merkle Tree is kept in a recursive data structure. Building is done bottom up,
* from all leaves' hashes. If number of leaves is not a power of two, the tree is padded with zero hashes.
*/
sealed class MerkleTree(val hash: SecureHash) {
class Leaf(val value: SecureHash) : MerkleTree(value)
class Node(val value: SecureHash, val left: MerkleTree, val right: MerkleTree) : MerkleTree(value)
companion object {
private fun isPow2(num: Int): Boolean = num and (num-1) == 0
/**
* Merkle tree building using hashes, with zero hash padding to full power of 2.
*/
@Throws(IllegalArgumentException::class)
fun getMerkleTree(allLeavesHashes: List<SecureHash>): MerkleTree {
val leaves = padWithZeros(allLeavesHashes).map { Leaf(it) }
return buildMerkleTree(leaves)
}
// If number of leaves in the tree is not a power of 2, we need to pad it with zero hashes.
private fun padWithZeros(allLeavesHashes: List<SecureHash>): List<SecureHash> {
var n = allLeavesHashes.size
if (isPow2(n)) return allLeavesHashes
val paddedHashes = ArrayList<SecureHash>(allLeavesHashes)
while (!isPow2(n++)) {
paddedHashes.add(SecureHash.zeroHash)
}
return paddedHashes
}
/**
* Tailrecursive function for building a tree bottom up.
* @param lastNodesList MerkleTree nodes from previous level.
* @return Tree root.
*/
private tailrec fun buildMerkleTree(lastNodesList: List<MerkleTree>): MerkleTree {
if (lastNodesList.isEmpty())
throw MerkleTreeException("Cannot calculate Merkle root on empty hash list.")
if (lastNodesList.size == 1) {
return lastNodesList[0] //Root reached.
} else {
val newLevelHashes: MutableList<MerkleTree> = ArrayList()
val n = lastNodesList.size
require((n and 1) == 0) { "Sanity check: number of nodes should be even." }
for (i in 0..n-2 step 2) {
val left = lastNodesList[i]
val right = lastNodesList[i+1]
val newHash = left.hash.hashConcat(right.hash)
val combined = Node(newHash, left, right)
newLevelHashes.add(combined)
}
return buildMerkleTree(newLevelHashes)
}
}
}
}

View File

@ -1,7 +1,7 @@
package net.corda.core.crypto
import net.corda.core.transactions.MerkleTree
import net.corda.core.transactions.hashConcat
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import java.util.*
@ -19,14 +19,12 @@ class MerkleTreeException(val reason: String) : Exception() {
* / \
* h14 h55
* / \ / \
* h12 h34 h5->d(h5)
* / \ / \ / \
* l1 l2 l3 l4 l5->d(l5)
* h12 h34 h50 h00
* / \ / \ / \ / \
* l1 l2 l3 l4 l5 0 0 0
*
* l* denote hashes of leaves, h* - hashes of nodes below.
* h5->d(h5) denotes duplication of the left hand side node. These nodes are kept in a full tree as DuplicatedLeaf.
* When filtering the tree for l5, we don't want to keep both l5 and its duplicate (it can also be solved using null
* values in a tree, but this solution is clearer).
* l* denote hashes of leaves, h* - hashes of nodes below. 0 denotes zero hash, we use it to pad not full binary trees,
* so the number of leaves is always a power of 2.
*
* Example of Partial tree based on the tree above.
*
@ -34,13 +32,13 @@ class MerkleTreeException(val reason: String) : Exception() {
* / \
* _ _
* / \ / \
* h12 _ _ d(h5)
* h12 _ _ h00
* / \ / \
* I3 l4 I5 d(l5)
* I3 l4 I5 0
*
* We want to check l3 and l5 - now turned into IncudedLeaf (I3 and I5 above). To verify that these two leaves belong to
* the tree with a hash root h15 we need to provide a Merkle branch (or partial tree). In our case we need hashes:
* h12, l4, d(l5) and d(h5). Verification is done by hashing the partial tree to obtain the root and checking it against
* h12, l4, 0 and h00. Verification is done by hashing the partial tree to obtain the root and checking it against
* the obtained h15 hash. Additionally we store included hashes used in calculation and compare them to leaves hashes we got
* (there can be a difference in obtained leaves ordering - that's why it's a set comparison not hashing leaves into a tree).
* If both equalities hold, we can assume that l3 and l5 belong to the transaction with root h15.
@ -54,7 +52,7 @@ class PartialMerkleTree(val root: PartialTree) {
* transaction and leaves that just keep hashes needed for calculation. Reason for this approach: during verification
* it's easier to extract hashes used as a base for this tree.
*/
sealed class PartialTree() {
sealed class PartialTree {
class IncludedLeaf(val hash: SecureHash) : PartialTree()
class Leaf(val hash: SecureHash) : PartialTree()
class Node(val left: PartialTree, val right: PartialTree) : PartialTree()
@ -66,15 +64,31 @@ class PartialMerkleTree(val root: PartialTree) {
* @param includeHashes Hashes that should be included in a partial tree.
* @return Partial Merkle tree root.
*/
@Throws(IllegalArgumentException::class, MerkleTreeException::class)
fun build(merkleRoot: MerkleTree, includeHashes: List<SecureHash>): PartialMerkleTree {
val usedHashes = ArrayList<SecureHash>()
require(zeroHash !in includeHashes) { "Zero hashes shouldn't be included in partial tree." }
checkFull(merkleRoot) // Throws MerkleTreeException if it is not a full binary tree.
val tree = buildPartialTree(merkleRoot, includeHashes, usedHashes)
//Too much included hashes or different ones.
// Too many included hashes or different ones.
if (includeHashes.size != usedHashes.size)
throw MerkleTreeException("Some of the provided hashes are not in the tree.")
return PartialMerkleTree(tree.second)
}
// Check if a MerkleTree is full binary tree. Returns the height of the tree if full, otherwise throws exception.
private fun checkFull(tree: MerkleTree, level: Int = 0): Int {
return when (tree) {
is MerkleTree.Leaf -> level
is MerkleTree.Node -> {
val l1 = checkFull(tree.left, level+1)
val l2 = checkFull(tree.right, level+1)
if (l1 != l2) throw MerkleTreeException("Got not full binary tree.")
l1
}
}
}
/**
* @param root Root of full Merkle tree which is a base for a partial one.
* @param includeHashes Hashes of leaves to be included in this partial tree.
@ -93,18 +107,17 @@ class PartialMerkleTree(val root: PartialTree) {
usedHashes.add(root.value)
Pair(true, PartialTree.IncludedLeaf(root.value))
} else Pair(false, PartialTree.Leaf(root.value))
is MerkleTree.DuplicatedLeaf -> Pair(false, PartialTree.Leaf(root.value))
is MerkleTree.Node -> {
val leftNode = buildPartialTree(root.left, includeHashes, usedHashes)
val rightNode = buildPartialTree(root.right, includeHashes, usedHashes)
if (leftNode.first or rightNode.first) {
//This node is on a path to some included leaves. Don't store hash.
// This node is on a path to some included leaves. Don't store hash.
val newTree = PartialTree.Node(leftNode.second, rightNode.second)
return Pair(true, newTree)
Pair(true, newTree)
} else {
//This node has no included leaves below. Cut the tree here and store a hash as a Leaf.
// This node has no included leaves below. Cut the tree here and store a hash as a Leaf.
val newTree = PartialTree.Leaf(root.value)
return Pair(false, newTree)
Pair(false, newTree)
}
}
}
@ -118,7 +131,7 @@ class PartialMerkleTree(val root: PartialTree) {
fun verify(merkleRootHash: SecureHash, hashesToCheck: List<SecureHash>): Boolean {
val usedHashes = ArrayList<SecureHash>()
val verifyRoot = verify(root, usedHashes)
//It means that we obtained more/less hashes than needed or different sets of hashes.
// It means that we obtained more/fewer hashes than needed or different sets of hashes.
if (hashesToCheck.groupBy { it } != usedHashes.groupBy { it })
return false
return (verifyRoot == merkleRootHash)

View File

@ -1,7 +1,5 @@
package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey
/**
@ -22,15 +20,8 @@ import java.security.PublicKey
*
* @see CompositeKey
*/
class Party(val name: String, val owningKey: CompositeKey) {
class Party(val name: String, owningKey: CompositeKey) : AnonymousParty(owningKey) {
/** 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)
/** 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
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes))
override fun toString() = "${owningKey.toBase58String()} (name)"
}

View File

@ -19,6 +19,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
override fun toString() = BaseEncoding.base16().encode(bytes)
fun prefixChars(prefixLen: Int = 6) = toString().substring(0, prefixLen)
fun hashConcat(other: SecureHash) = (this.bytes + other.bytes).sha256()
// Like static methods in Java, except the 'companion' is a singleton that can have state.
companion object {
@ -35,6 +36,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
@JvmStatic fun sha256(str: String) = sha256(str.toByteArray())
@JvmStatic fun randomSHA256() = sha256(newSecureRandom().generateSeed(32))
val zeroHash = SecureHash.SHA256(ByteArray(32, { 0.toByte() }))
}
// In future, maybe SHA3, truncated hashes etc.

View File

@ -9,5 +9,8 @@ package net.corda.core.flows
* [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)
open class FlowException(override val message: String?, override val cause: Throwable?) : Exception() {
constructor(message: String?) : this(message, null)
constructor(cause: Throwable?) : this(cause?.toString(), cause)
constructor() : this(null, null)
}

View File

@ -2,7 +2,9 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
import org.slf4j.Logger
@ -171,6 +173,16 @@ abstract class FlowLogic<out T> {
}
}
/**
* Suspends the flow until the transaction with the specified ID is received, successfully verified and
* sent to the vault for processing. Note that this call suspends until the transaction is considered
* valid by the local node, but that doesn't imply the vault will consider it relevant.
*/
@Suspendable
fun waitForLedgerCommit(hash: SecureHash): SignedTransaction {
return stateMachine.waitForLedgerCommit(hash, this)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
private var _stateMachine: FlowStateMachine<*>? = null
@ -191,7 +203,7 @@ abstract class FlowLogic<out T> {
val theirs = subLogic.progressTracker
if (ours != null && theirs != null) {
if (ours.currentStep == ProgressTracker.UNSTARTED) {
logger.warn("ProgressTracker has not been started for $this")
logger.warn("ProgressTracker has not been started")
ours.nextStep()
}
ours.setChildProgressTracker(ours.currentStep, theirs)

View File

@ -8,6 +8,7 @@ import java.lang.reflect.Type
import java.util.*
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.jvm.javaConstructor
import kotlin.reflect.jvm.javaType
import kotlin.reflect.primaryConstructor
@ -63,13 +64,37 @@ class FlowLogicRefFactory(private val flowWhitelist: Map<String, Set<String>>) :
}
/**
* Create a [FlowLogicRef] for the Kotlin primary constructor or Java constructor and the given args.
* Create a [FlowLogicRef] by matching against the available constructors and the given args.
*/
fun create(type: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
val constructor = type.kotlin.primaryConstructor ?: return createJava(type, *args)
if (constructor.parameters.size < args.size) {
throw IllegalFlowLogicException(type, "due to too many arguments supplied to kotlin primary constructor")
// If it's not a Kotlin class, do the Java path.
if (type.kotlin.primaryConstructor == null)
return createJava(type, *args)
// Find the right constructor to use, based on passed argument types. This is for when we don't know
// the right argument names.
//
// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly
// to avoid guessing which constructor too use.
val argTypes = args.map { it?.javaClass }
val constructor = try {
type.kotlin.constructors.single { ctor ->
// Get the types of the arguments, always boxed (as that's what we get in the invocation).
val ctorTypes = ctor.javaConstructor!!.parameterTypes.map { Primitives.wrap(it) }
if (argTypes.size != ctorTypes.size)
return@single false
for ((argType, ctorType) in argTypes.zip(ctorTypes)) {
if (argType == null) continue // Try and find a match based on the other arguments.
if (!ctorType.isAssignableFrom(argType)) return@single false
}
true
}
} catch (e: IllegalArgumentException) {
throw IllegalFlowLogicException(type, "due to ambiguous match against the constructors: $argTypes")
} catch (e: NoSuchElementException) {
throw IllegalFlowLogicException(type, "due to missing constructor for arguments: $argTypes")
}
// Build map of args from array
val argsMap = args.zip(constructor.parameters).map { Pair(it.second.name!!, it.first) }.toMap()
return createKotlin(type, argsMap)

View File

@ -3,7 +3,9 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.UntrustworthyData
import org.slf4j.Logger
import java.util.*
@ -35,6 +37,9 @@ interface FlowStateMachine<R> {
@Suspendable
fun send(otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>)
@Suspendable
fun waitForLedgerCommit(hash: SecureHash, sessionFlow: FlowLogic<*>): SignedTransaction
val serviceHub: ServiceHub
val logger: Logger
val id: StateMachineRunId

View File

@ -3,6 +3,7 @@ package net.corda.core.messaging
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UpgradedContract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
@ -107,6 +108,20 @@ interface CordaRPCOps : RPCOps {
@Deprecated("This service will be removed in a future milestone")
fun uploadFile(dataType: String, name: String?, file: InputStream): String
/**
* Authorise a contract state upgrade.
* This will store the upgrade authorisation in the vault, and will be queried by [ContractUpgradeFlow.Acceptor] during contract upgrade process.
* Invoking this method indicate the node is willing to upgrade the [state] using the [upgradedContractClass].
* This method will NOT initiate the upgrade process. To start the upgrade process, see [ContractUpgradeFlow.Instigator].
*/
fun authoriseContractUpgrade(state: StateAndRef<*>, upgradedContractClass: Class<UpgradedContract<*, *>>)
/**
* Authorise a contract state upgrade.
* This will remove the upgrade authorisation from the vault.
*/
fun deauthoriseContractUpgrade(state: StateAndRef<*>)
/**
* Returns the node's current time.
*/

View File

@ -43,7 +43,8 @@ interface MessagingService {
/**
* The provided function will be invoked for each received message whose topic and session matches. The callback
* will run on threads provided by the messaging service, and the callback is expected to be thread safe as a result.
* will run on the main server thread provided when the messaging service is constructed, and a database
* transaction is set up for you automatically.
*
* The returned object is an opaque handle that may be used to un-register handlers later with [removeMessageHandler].
* The handle is passed to the callback as well, to avoid race conditions whereby the callback wants to unregister

View File

@ -29,11 +29,12 @@ interface ServiceHub {
val myInfo: NodeInfo
/**
* Given a list of [SignedTransaction]s, writes them to the local storage for validated transactions and then
* sends them to the vault for further processing.
* Given a [SignedTransaction], writes it to the local storage for validated transactions and then
* sends them to the vault for further processing. Expects to be run within a database transaction.
*
* @param txs The transactions to record.
*/
// TODO: Make this take a single tx.
fun recordTransactions(txs: Iterable<SignedTransaction>)
/**

View File

@ -1,5 +1,7 @@
package net.corda.core.node.services
import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
@ -10,6 +12,7 @@ import net.corda.core.crypto.Party
*/
interface IdentityService {
fun registerIdentity(party: Party)
/**
* Get all identities known to the service. This is expensive, and [partyFromKey] or [partyFromName] should be
* used in preference where possible.
@ -22,4 +25,7 @@ interface IdentityService {
fun partyFromKey(key: CompositeKey): Party?
fun partyFromName(name: String): Party?
fun partyFromAnonymous(party: AnonymousParty): Party?
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
}

View File

@ -2,15 +2,11 @@ package net.corda.core.node.services
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.crypto.*
import net.corda.core.toFuture
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import rx.Observable
import java.io.File
import java.io.InputStream
import java.security.KeyPair
import java.security.PrivateKey
@ -146,7 +142,7 @@ interface VaultService {
fun statesForRefs(refs: List<StateRef>): Map<StateRef, TransactionState<*>?> {
val refsToStates = currentVault.states.associateBy { it.ref }
return refs.associateBy({ it }, { refsToStates[it]?.state })
return refs.associateBy({ it }) { refsToStates[it]?.state }
}
/**
@ -167,6 +163,24 @@ interface VaultService {
return updates.filter { it.consumed.any { it.ref == ref } }.toFuture()
}
/** Get contracts we would be willing to upgrade the suggested contract to. */
// TODO: We need a better place to put business logic functions
fun getAuthorisedContractUpgrade(ref: StateRef): Class<UpgradedContract<*, *>>?
/**
* Authorise a contract state upgrade.
* This will store the upgrade authorisation in the vault, and will be queried by [ContractUpgradeFlow.Acceptor] during contract upgrade process.
* Invoking this method indicate the node is willing to upgrade the [state] using the [upgradedContractClass].
* This method will NOT initiate the upgrade process. To start the upgrade process, see [ContractUpgradeFlow.Instigator].
*/
fun authoriseContractUpgrade(stateAndRef: StateAndRef<*>, upgradedContractClass: Class<UpgradedContract<*, *>>)
/**
* Authorise a contract state upgrade.
* This will remove the upgrade authorisation from the vault.
*/
fun deauthoriseContractUpgrade(stateAndRef: StateAndRef<*>)
/**
* Add a note to an existing [LedgerTransaction] given by its unique [SecureHash] id
* Multiple notes may be attached to the same [LedgerTransaction].
@ -178,21 +192,32 @@ interface VaultService {
fun getTransactionNotes(txnId: SecureHash): Iterable<String>
/**
* [InsufficientBalanceException] is thrown when a Cash Spending transaction fails because
* there is insufficient quantity for a given currency (and optionally set of Issuer Parties).
* Note: an [Amount] of [Currency] is only fungible for a given Issuer Party within a [FungibleAsset]
**/
* Generate a transaction that moves an amount of currency to the given pubkey.
*
* Note: an [Amount] of [Currency] is only fungible for a given Issuer Party within a [FungibleAsset]
*
* @param tx A builder, which may contain inputs, outputs and commands already. The relevant components needed
* to move the cash will be added on top.
* @param amount How much currency to send.
* @param to a key of the recipient.
* @param onlyFromParties if non-null, the asset states will be filtered to only include those issued by the set
* of given parties. This can be useful if the party you're trying to pay has expectations
* about which type of asset claims they are willing to accept.
* @return A [Pair] of the same transaction builder passed in as [tx], and the list of keys that need to sign
* the resulting transaction for it to be valid.
* @throws InsufficientBalanceException when a cash spending transaction fails because
* there is insufficient quantity for a given currency (and optionally set of Issuer Parties).
*/
@Throws(InsufficientBalanceException::class)
fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>,
to: CompositeKey,
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<CompositeKey>>
onlyFromParties: Set<AnonymousParty>? = null): Pair<TransactionBuilder, List<CompositeKey>>
}
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
inline fun <reified T : DealState> VaultService.dealsWith(party: Party) = linearHeadsOfType<T>().values.filter {
// TODO: Replace name comparison with full party comparison (keys are currenty not equal)
it.state.data.parties.any { it.name == party.name }
it.state.data.parties.any { it == party }
}
/**

View File

@ -54,6 +54,7 @@ class LedgerTransaction(
*
* @throws TransactionVerificationException if anything goes wrong.
*/
@Throws(TransactionVerificationException::class)
fun verify() = type.verify(this)
// TODO: When we upgrade to Kotlin 1.1 we can make this a data class again and have the compiler generate these.

View File

@ -1,149 +1,91 @@
package net.corda.core.transactions
import net.corda.core.contracts.Command
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.createKryo
import net.corda.core.serialization.extendKryoHash
import net.corda.core.serialization.serialize
import java.util.*
/**
* Build filtered transaction using provided filtering functions.
*/
fun WireTransaction.buildFilteredTransaction(filterFuns: FilterFuns): FilteredTransaction {
return FilteredTransaction.buildMerkleTransaction(this, filterFuns)
}
/**
* Calculation of all leaves hashes that are needed for calculation of transaction id and partial Merkle branches.
*/
fun WireTransaction.calculateLeavesHashes(): List<SecureHash> {
val resultHashes = ArrayList<SecureHash>()
val entries = listOf(inputs, outputs, attachments, commands)
entries.forEach { it.mapTo(resultHashes, { x -> serializedHash(x) }) }
return resultHashes
}
fun SecureHash.hashConcat(other: SecureHash) = (this.bytes + other.bytes).sha256()
fun <T : Any> serializedHash(x: T): SecureHash {
val kryo = extendKryoHash(createKryo()) //Dealing with HashMaps inside states.
val kryo = extendKryoHash(createKryo()) // Dealing with HashMaps inside states.
return x.serialize(kryo).hash
}
/**
* Creation and verification of a Merkle Tree for a Wire Transaction.
*
* See: https://en.wikipedia.org/wiki/Merkle_tree
*
* Transaction is split into following blocks: inputs, outputs, commands, attachments' refs. Merkle Tree is kept in
* a recursive data structure. Building is done bottom up, from all leaves' hashes.
* If a row in a tree has an odd number of elements - the final hash is hashed with itself.
* Interface implemented by WireTransaction and FilteredLeaves.
* Property traversableList assures that we always calculate hashes in the same order, lets us define which
* fields of WireTransaction will be included in id calculation or partial merkle tree building.
*/
sealed class MerkleTree(val hash: SecureHash) {
class Leaf(val value: SecureHash) : MerkleTree(value)
class Node(val value: SecureHash, val left: MerkleTree, val right: MerkleTree) : MerkleTree(value)
//DuplicatedLeaf is storing a hash of the rightmost node that had to be duplicated to obtain the tree.
//That duplication can cause problems while building and verifying partial tree (especially for trees with duplicate
//attachments or commands).
class DuplicatedLeaf(val value: SecureHash) : MerkleTree(value)
interface TraversableTransaction {
val inputs: List<StateRef>
val attachments: List<SecureHash>
val outputs: List<TransactionState<ContractState>>
val commands: List<Command>
val notary: Party?
val mustSign: List<CompositeKey>
val type: TransactionType?
val timestamp: Timestamp?
fun hashNodes(right: MerkleTree): MerkleTree {
val newHash = this.hash.hashConcat(right.hash)
return Node(newHash, this, right)
}
companion object {
/**
* Merkle tree building using hashes.
*/
fun getMerkleTree(allLeavesHashes: List<SecureHash>): MerkleTree {
val leaves = allLeavesHashes.map { MerkleTree.Leaf(it) }
return buildMerkleTree(leaves)
/**
* Traversing transaction fields with a list function over transaction contents. Used for leaves hashes calculation
* and user provided filtering and checking of filtered transaction.
*/
// We may want to specify our own behaviour on certain tx fields.
// Like if we include them at all, what to do with null values, if we treat list as one or not etc. for building
// torn-off transaction and id calculation.
val traversableList: List<Any>
get() {
val traverseList = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList()
if (notary != null) traverseList.add(notary!!)
traverseList.addAll(mustSign)
if (type != null) traverseList.add(type!!)
if (timestamp != null) traverseList.add(timestamp!!)
return traverseList
}
/**
* Tailrecursive function for building a tree bottom up.
* @param lastNodesList MerkleTree nodes from previous level.
* @return Tree root.
*/
private tailrec fun buildMerkleTree(lastNodesList: List<MerkleTree>): MerkleTree {
if (lastNodesList.size < 1)
throw MerkleTreeException("Cannot calculate Merkle root on empty hash list.")
if (lastNodesList.size == 1) {
return lastNodesList[0] //Root reached.
} else {
val newLevelHashes: MutableList<MerkleTree> = ArrayList()
var i = 0
while (i < lastNodesList.size) {
val left = lastNodesList[i]
val n = lastNodesList.size
// If there is an odd number of elements at this level,
// the last element is hashed with itself and stored as a Leaf.
val right = when {
i + 1 > n - 1 -> MerkleTree.DuplicatedLeaf(lastNodesList[n - 1].hash)
else -> lastNodesList[i + 1]
}
val combined = left.hashNodes(right)
newLevelHashes.add(combined)
i += 2
}
return buildMerkleTree(newLevelHashes)
}
}
}
// Calculation of all leaves hashes that are needed for calculation of transaction id and partial Merkle branches.
fun calculateLeavesHashes(): List<SecureHash> = traversableList.map { serializedHash(it) }
}
/**
* Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaves types.
* Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every
* field from WireTransaction can be used in PartialMerkleTree calculation.
*/
class FilteredLeaves(
val inputs: List<StateRef>,
val outputs: List<TransactionState<ContractState>>,
val attachments: List<SecureHash>,
val commands: List<Command>
) {
fun getFilteredHashes(): List<SecureHash> {
val resultHashes = ArrayList<SecureHash>()
val entries = listOf(inputs, outputs, attachments, commands)
entries.forEach { it.mapTo(resultHashes, { x -> serializedHash(x) }) }
return resultHashes
}
}
/**
* Holds filter functions on transactions fields.
* Functions are used to build a partial tree only out of some subset of original transaction fields.
*/
class FilterFuns(
val filterInputs: (StateRef) -> Boolean = { false },
val filterOutputs: (TransactionState<ContractState>) -> Boolean = { false },
val filterAttachments: (SecureHash) -> Boolean = { false },
val filterCommands: (Command) -> Boolean = { false }
) {
fun <T : Any> genericFilter(elem: T): Boolean {
return when (elem) {
is StateRef -> filterInputs(elem)
is TransactionState<*> -> filterOutputs(elem)
is SecureHash -> filterAttachments(elem)
is Command -> filterCommands(elem)
else -> throw IllegalArgumentException("Wrong argument type: ${elem.javaClass}")
}
override val inputs: List<StateRef>,
override val attachments: List<SecureHash>,
override val outputs: List<TransactionState<ContractState>>,
override val commands: List<Command>,
override val notary: Party?,
override val mustSign: List<CompositeKey>,
override val type: TransactionType?,
override val timestamp: Timestamp?
) : TraversableTransaction {
/**
* Function that checks the whole filtered structure.
* Force type checking on a structure that we obtained, so we don't sign more than expected.
* Example: Oracle is implemented to check only for commands, if it gets an attachment and doesn't expect it - it can sign
* over a transaction with the attachment that wasn't verified. Of course it depends on how you implement it, but else -> false
* should solve a problem with possible later extensions to WireTransaction.
* @param checkingFun function that performs type checking on the structure fields and provides verification logic accordingly.
* @returns false if no elements were matched on a structure or checkingFun returned false.
*/
fun checkWithFun(checkingFun: (Any) -> Boolean): Boolean {
val checkList = traversableList.map { checkingFun(it) }
return (!checkList.isEmpty()) && checkList.all { true }
}
}
/**
* Class representing merkleized filtered transaction.
* @param rootHash Merkle tree root hash.
* @param filteredLeaves Leaves included in a filtered transaction.
* @param partialMerkleTree Merkle branch needed to verify filteredLeaves.
*/
class FilteredTransaction(
class FilteredTransaction private constructor(
val rootHash: SecureHash,
val filteredLeaves: FilteredLeaves,
val partialMerkleTree: PartialMerkleTree
) {
@ -151,29 +93,34 @@ class FilteredTransaction(
/**
* Construction of filtered transaction with Partial Merkle Tree.
* @param wtx WireTransaction to be filtered.
* @param filterFuns filtering functions for inputs, outputs, attachments, commands.
* @param filtering filtering over the whole WireTransaction
*/
fun buildMerkleTransaction(wtx: WireTransaction,
filterFuns: FilterFuns
filtering: (Any) -> Boolean
): FilteredTransaction {
val filteredInputs = wtx.inputs.filter { filterFuns.genericFilter(it) }
val filteredOutputs = wtx.outputs.filter { filterFuns.genericFilter(it) }
val filteredAttachments = wtx.attachments.filter { filterFuns.genericFilter(it) }
val filteredCommands = wtx.commands.filter { filterFuns.genericFilter(it) }
val filteredLeaves = FilteredLeaves(filteredInputs, filteredOutputs, filteredAttachments, filteredCommands)
val pmt = PartialMerkleTree.build(wtx.merkleTree, filteredLeaves.getFilteredHashes())
return FilteredTransaction(filteredLeaves, pmt)
val filteredLeaves = wtx.filterWithFun(filtering)
val merkleTree = wtx.getMerkleTree()
val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.calculateLeavesHashes())
return FilteredTransaction(merkleTree.hash, filteredLeaves, pmt)
}
}
/**
* Runs verification of Partial Merkle Branch with merkleRootHash.
* Runs verification of Partial Merkle Branch against [rootHash].
*/
fun verify(merkleRootHash: SecureHash): Boolean {
val hashes: List<SecureHash> = filteredLeaves.getFilteredHashes()
if (hashes.size == 0)
@Throws(MerkleTreeException::class)
fun verify(): Boolean {
val hashes: List<SecureHash> = filteredLeaves.calculateLeavesHashes()
if (hashes.isEmpty())
throw MerkleTreeException("Transaction without included leaves.")
return partialMerkleTree.verify(merkleRootHash, hashes)
return partialMerkleTree.verify(rootHash, hashes)
}
/**
* Runs verification of Partial Merkle Branch against [rootHash]. Checks filteredLeaves with provided checkingFun.
*/
@Throws(MerkleTreeException::class)
fun verifyWithFunction(checkingFun: (Any) -> Boolean): Boolean {
return verify() && filteredLeaves.checkWithFun { checkingFun(it) }
}
}

View File

@ -1,5 +1,6 @@
package net.corda.core.transactions
import net.corda.core.contracts.AttachmentResolutionException
import net.corda.core.contracts.NamedByHash
import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.crypto.CompositeKey
@ -8,7 +9,6 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.signWithECDSA
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SerializedBytes
import java.io.FileNotFoundException
import java.security.KeyPair
import java.security.SignatureException
import java.util.*
@ -127,12 +127,12 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
* [WireTransaction.toLedgerTransaction] with the passed in [ServiceHub] to resolve the dependencies,
* returning an unverified LedgerTransaction.
*
* @throws FileNotFoundException if a required attachment was not found in storage.
* @throws AttachmentResolutionException if a required attachment was not found in storage.
* @throws TransactionResolutionException if an input points to a transaction not found in storage.
* @throws SignatureException if any signatures were invalid or unrecognised
* @throws SignaturesMissingException if any signatures that should have been present are missing.
*/
@Throws(FileNotFoundException::class, TransactionResolutionException::class, SignaturesMissingException::class)
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, SignatureException::class)
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)
/**

View File

@ -3,6 +3,7 @@ package net.corda.core.transactions
import com.esotericsoftware.kryo.Kryo
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.indexOfOrThrow
@ -12,7 +13,6 @@ import net.corda.core.serialization.THREAD_LOCAL_KRYO
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.utilities.Emoji
import java.io.FileNotFoundException
import java.security.PublicKey
/**
@ -25,15 +25,15 @@ class WireTransaction(
/** Pointers to the input states on the ledger, identified by (tx identity hash, output index). */
override val inputs: List<StateRef>,
/** Hashes of the ZIP/JAR files that are needed to interpret the contents of this wire transaction. */
val attachments: List<SecureHash>,
override val attachments: List<SecureHash>,
outputs: List<TransactionState<ContractState>>,
/** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */
val commands: List<Command>,
override val commands: List<Command>,
notary: Party?,
signers: List<CompositeKey>,
type: TransactionType,
timestamp: Timestamp?
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp), TraversableTransaction {
init {
checkInvariants()
}
@ -42,14 +42,7 @@ class WireTransaction(
@Volatile @Transient private var cachedBytes: SerializedBytes<WireTransaction>? = null
val serialized: SerializedBytes<WireTransaction> get() = cachedBytes ?: serialize().apply { cachedBytes = this }
//We need cashed leaves hashes and whole tree for an id and Partial Merkle Tree calculation.
@Volatile @Transient private var cachedLeavesHashes: List<SecureHash>? = null
val allLeavesHashes: List<SecureHash> get() = cachedLeavesHashes ?: calculateLeavesHashes().apply { cachedLeavesHashes = this }
@Volatile @Transient var cachedTree: MerkleTree? = null
val merkleTree: MerkleTree get() = cachedTree ?: MerkleTree.getMerkleTree(allLeavesHashes).apply { cachedTree = this }
override val id: SecureHash get() = merkleTree.hash
override val id: SecureHash by lazy { getMerkleTree().hash }
companion object {
fun deserialize(data: SerializedBytes<WireTransaction>, kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction {
@ -73,10 +66,10 @@ class WireTransaction(
* Looks up identities and attachments from storage to generate a [LedgerTransaction]. A transaction is expected to
* have been fully resolved using the resolution flow by this point.
*
* @throws FileNotFoundException if a required attachment was not found in storage.
* @throws AttachmentResolutionException if a required attachment was not found in storage.
* @throws TransactionResolutionException if an input points to a transaction not found in storage.
*/
@Throws(FileNotFoundException::class, TransactionResolutionException::class)
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
fun toLedgerTransaction(services: ServiceHub): LedgerTransaction {
// Look up public keys to authenticated identities. This is just a stub placeholder and will all change in future.
val authenticatedArgs = commands.map {
@ -85,12 +78,45 @@ class WireTransaction(
}
// Open attachments specified in this transaction. If we haven't downloaded them, we fail.
val attachments = attachments.map {
services.storageService.attachments.openAttachment(it) ?: throw FileNotFoundException(it.toString())
services.storageService.attachments.openAttachment(it) ?: throw AttachmentResolutionException(it)
}
val resolvedInputs = inputs.map { StateAndRef(services.loadState(it), it) }
return LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, mustSign, timestamp, type)
}
/**
* Build filtered transaction using provided filtering functions.
*/
fun buildFilteredTransaction(filtering: (Any) -> Boolean): FilteredTransaction {
return FilteredTransaction.buildMerkleTransaction(this, filtering)
}
/**
* Builds whole Merkle tree for a transaction.
*/
fun getMerkleTree(): MerkleTree {
return MerkleTree.getMerkleTree(calculateLeavesHashes())
}
/**
* Construction of partial transaction from WireTransaction based on filtering.
* @param filtering filtering over the whole WireTransaction
* @returns FilteredLeaves used in PartialMerkleTree calculation and verification.
*/
fun filterWithFun(filtering: (Any) -> Boolean): FilteredLeaves {
fun notNullFalse(elem: Any?): Any? = if(elem == null || !filtering(elem)) null else elem
return FilteredLeaves(
inputs.filter { filtering(it) },
attachments.filter { filtering(it) },
outputs.filter { filtering(it) },
commands.filter { filtering(it) },
notNullFalse(notary) as Party?,
mustSign.filter { filtering(it) },
notNullFalse(type) as TransactionType?,
notNullFalse(timestamp) as Timestamp?
)
}
override fun toString(): String {
val buf = StringBuilder()
buf.appendln("Transaction $id:")

View File

@ -18,11 +18,18 @@ class UntrustworthyData<out T>(private val fromUntrustedWorld: T) {
@Deprecated("Accessing the untrustworthy data directly without validating it first is a bad idea")
get() = fromUntrustedWorld
@Suppress("DEPRECATION")
@Throws(FlowException::class)
inline fun <R> unwrap(validator: (T) -> R) = validator(data)
fun <R> unwrap(validator: Validator<T, R>) = validator.validate(fromUntrustedWorld)
@Suppress("DEPRECATION")
@Deprecated("This old name was confusing, use unwrap instead", replaceWith = ReplaceWith("unwrap"))
inline fun <R> validate(validator: (T) -> R) = validator(data)
interface Validator<in T, out R> {
@Throws(FlowException::class)
fun validate(data: T): R
}
}
@Suppress("DEPRECATION")
inline fun <T, R> UntrustworthyData<T>.unwrap(validator: (T) -> R): R = validator(data)

View File

@ -15,26 +15,36 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
import net.corda.flows.AbstractStateReplacementFlow.Acceptor
import net.corda.flows.AbstractStateReplacementFlow.Instigator
import net.corda.core.utilities.unwrap
/**
* Abstract flow to be used for replacing one state with another, for example when changing the notary of a state.
* Notably this requires a one to one replacement of states, states cannot be split, merged or issued as part of these
* flows.
*
* The [Instigator] assembles the transaction for state replacement and sends out change proposals to all participants
* ([Acceptor]) of that state. If participants agree to the proposed change, they each sign the transaction.
* 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.
*/
abstract class AbstractStateReplacementFlow {
data class Proposal<out T>(val stateRef: StateRef, val modification: T, val stx: SignedTransaction)
/**
* The [Proposal] contains the details of proposed state modification.
* This is the message sent by the [Instigator] to all participants([Acceptor]) during the state replacement process.
*
* @param M the type of a class representing proposed modification by the instigator.
*/
data class Proposal<out M>(val stateRef: StateRef, val modification: M, val stx: SignedTransaction)
abstract class Instigator<out S : ContractState, out T>(
/**
* The [Instigator] assembles the transaction for state replacement and sends out change proposals to all participants
* ([Acceptor]) of that state. If participants agree to the proposed change, they each sign the transaction.
* Finally, [Instigator] sends the transaction containing all participants' signatures to the notary for signature, and
* then back to each participant so they can record it and use the new updated state for future transactions.
*
* @param S the input contract state type
* @param T the output contract state type, this can be different from [S]. For example, in contract upgrade, the output state type can be different from the input state type after the upgrade process.
* @param M the type of a class representing proposed modification by the instigator.
*/
abstract class Instigator<out S : ContractState, out T : ContractState, out M>(
val originalState: StateAndRef<S>,
val modification: T,
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<StateAndRef<S>>() {
val modification: M,
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<StateAndRef<T>>() {
companion object {
object SIGNING : ProgressTracker.Step("Requesting signatures from other parties")
object NOTARY : ProgressTracker.Step("Requesting notary signature")
@ -44,7 +54,7 @@ abstract class AbstractStateReplacementFlow {
@Suspendable
@Throws(StateReplacementException::class)
override fun call(): StateAndRef<S> {
override fun call(): StateAndRef<T> {
val (stx, participants) = assembleTx()
progressTracker.currentStep = SIGNING

View File

@ -3,13 +3,13 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions
import net.corda.core.transactions.SignedTransaction
/**
* Notify all involved parties about a transaction, including storing a copy. Normally this would be called via
* [FinalityFlow].
* Notify the specified parties about a transaction. The remote peers will download this transaction and its
* dependency graph, verifying them all. The flow returns when all peers have acknowledged the transactions
* as valid. Normally you wouldn't use this directly, it would be called via [FinalityFlow].
*
* @param notarisedTransaction transaction which has been notarised (if needed) and is ready to notify nodes about.
* @param participants a list of participants involved in the transaction.
@ -17,17 +17,14 @@ import net.corda.core.transactions.SignedTransaction
*/
class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction,
val participants: Set<Party>) : FlowLogic<Unit>() {
data class NotifyTxRequest(val tx: SignedTransaction)
@Suspendable
override fun call() {
// Record it locally
serviceHub.recordTransactions(notarisedTransaction)
// TODO: Messaging layer should handle this broadcast for us
val msg = NotifyTxRequest(notarisedTransaction)
participants.filter { it != serviceHub.myInfo.legalIdentity }.forEach { participant ->
// This pops out the other side in DataVending.NotifyTransactionHandler.
send(participant, msg)
}
}

View File

@ -0,0 +1,83 @@
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.flows.AbstractStateReplacementFlow.Proposal
import net.corda.flows.ContractUpgradeFlow.Acceptor
import net.corda.flows.ContractUpgradeFlow.Instigator
/**
* A flow to be used for upgrading state objects of an old contract to a new contract.
*
* The [Instigator] assembles the transaction for contract upgrade and sends out change proposals to all participants
* ([Acceptor]) of that state. If participants agree to the proposed change, they each sign the transaction.
* 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.
*/
object ContractUpgradeFlow {
@JvmStatic
fun verify(tx: TransactionForContract) {
// Contract Upgrade transaction should have 1 input, 1 output and 1 command.
verify(tx.inputs.single(), tx.outputs.single(), tx.commands.map { Command(it.value, it.signers) }.single())
}
@JvmStatic
fun verify(input: ContractState, output: ContractState, commandData: Command) {
val command = commandData.value as UpgradeCommand
val participants: Set<CompositeKey> = input.participants.toSet()
val keysThatSigned: Set<CompositeKey> = commandData.signers.toSet()
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
requireThat {
"The signing keys include all participant keys" by keysThatSigned.containsAll(participants)
"Inputs state reference the legacy contract" by (input.contract.javaClass == upgradedContract.legacyContract.javaClass)
"Outputs state reference the upgraded contract" by (output.contract.javaClass == command.upgradedContractClass)
"Output state must be an upgraded version of the input state" by (output == upgradedContract.upgrade(input))
}
}
private fun <OldState : ContractState, NewState : ContractState> assembleBareTx(
stateRef: StateAndRef<OldState>,
upgradedContractClass: Class<UpgradedContract<OldState, NewState>>
): TransactionBuilder {
val contractUpgrade = upgradedContractClass.newInstance()
return TransactionType.General.Builder(stateRef.state.notary)
.withItems(stateRef, contractUpgrade.upgrade(stateRef.state.data), Command(UpgradeCommand(contractUpgrade.javaClass), stateRef.state.data.participants))
}
class Instigator<OldState : ContractState, NewState : ContractState>(
originalState: StateAndRef<OldState>,
newContractClass: Class<UpgradedContract<OldState, NewState>>
) : AbstractStateReplacementFlow.Instigator<OldState, NewState, Class<UpgradedContract<OldState, NewState>>>(originalState, newContractClass) {
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> {
val stx = assembleBareTx(originalState, modification)
.signWith(serviceHub.legalIdentityKey)
.toSignedTransaction(false)
return Pair(stx, originalState.state.data.participants)
}
}
class Acceptor(otherSide: Party) : AbstractStateReplacementFlow.Acceptor<Class<UpgradedContract<ContractState, *>>>(otherSide) {
@Suspendable
@Throws(StateReplacementException::class)
override fun verifyProposal(proposal: Proposal<Class<UpgradedContract<ContractState, *>>>) {
// Retrieve signed transaction from our side, we will apply the upgrade logic to the transaction on our side, and verify outputs matches the proposed upgrade.
val stx = subFlow(FetchTransactionsFlow(setOf(proposal.stateRef.txhash), otherSide)).fromDisk.singleOrNull()
requireNotNull(stx) { "We don't have a copy of the referenced state" }
val oldStateAndRef = stx!!.tx.outRef<ContractState>(proposal.stateRef.index)
val authorisedUpgrade = serviceHub.vaultService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?: throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}")
val proposedTx = proposal.stx.tx
val expectedTx = assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
requireThat {
"The instigator is one of the participants" by oldStateAndRef.state.data.participants.contains(otherSide.owningKey)
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" by (proposal.modification == authorisedUpgrade)
"The proposed tx matches the expected tx for this upgrade" by (proposedTx == expectedTx)
}
ContractUpgradeFlow.verify(oldStateAndRef.state.data, expectedTx.outRef<ContractState>(0).state.data, expectedTx.commands.single())
}
}
}

View File

@ -7,6 +7,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.unwrap
import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch
import net.corda.flows.FetchDataFlow.HashNotFound
import java.util.*

View File

@ -1,48 +1,87 @@
package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
/**
* Finalise a transaction by notarising it, then recording it locally, and then sending it to all involved parties.
* Verifies the given transactions, then sends them to the named notaries. If the notary agrees that the transactions
* are acceptable then they are from that point onwards committed to the ledger, and will be written through to the
* vault. Additionally they will be distributed to the parties reflected in the participants list of the states.
*
* @param transaction to commit.
* @param participants a list of participants involved in the transaction.
* @return a list of participants who were successfully notified of the transaction.
* The transactions will be topologically sorted before commitment to ensure that dependencies are committed before
* dependers, so you don't need to do this yourself.
*
* The transactions are expected to have already been resolved: if their dependencies are not available in local
* storage or within the given set, verification will fail. They must have signatures from all necessary parties
* other than the notary.
*
* If specified, the extra recipients are sent all the given transactions. The base set of parties to inform of each
* transaction are calculated on a per transaction basis from the contract-given set of participants.
*
* The flow returns the same transactions, in the same order, with the additional signatures.
*
* @param transactions What to commit.
* @param extraRecipients A list of additional participants to inform of the transaction.
*/
class FinalityFlow(val transaction: SignedTransaction,
val participants: Set<Party>,
override val progressTracker: ProgressTracker) : FlowLogic<Unit>() {
constructor(transaction: SignedTransaction, participants: Set<Party>) : this(transaction, participants, tracker())
class FinalityFlow(val transactions: Iterable<SignedTransaction>,
val extraRecipients: Set<Party>,
override val progressTracker: ProgressTracker) : FlowLogic<List<SignedTransaction>>() {
constructor(transaction: SignedTransaction, extraParticipants: Set<Party>) : this(listOf(transaction), extraParticipants, tracker())
constructor(transaction: SignedTransaction) : this(listOf(transaction), emptySet(), tracker())
companion object {
object NOTARISING : ProgressTracker.Step("Requesting signature by notary service")
object NOTARISING : ProgressTracker.Step("Requesting signature by notary service") {
override fun childProgressTracker() = NotaryFlow.Client.tracker()
}
object BROADCASTING : ProgressTracker.Step("Broadcasting transaction to participants")
// TODO: Make all tracker() methods @JvmStatic
fun tracker() = ProgressTracker(NOTARISING, BROADCASTING)
}
@Suspendable
@Throws(NotaryException::class)
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.
override fun call(): List<SignedTransaction> {
// Note: this method is carefully broken up to minimize the amount of data reachable from the stack at
// the point where subFlow is invoked, as that minimizes the checkpointing work to be done.
//
// Lookup the resolved transactions and use them to map each signed transaction to the list of participants.
// Then send to the notary if needed, record locally and distribute.
progressTracker.currentStep = NOTARISING
// Notarise the transaction if needed
val notarisedTransaction = if (needsNotarySignature(transaction)) {
val notarySig = subFlow(NotaryFlow.Client(transaction))
transaction.withAdditionalSignature(notarySig)
} else {
transaction
}
val notarisedTxns = notariseAndRecord(lookupParties(resolveDependenciesOf(transactions)))
// Let everyone else know about the transaction
// Each transaction has its own set of recipients, but extra recipients get them all.
progressTracker.currentStep = BROADCASTING
subFlow(BroadcastTransactionFlow(notarisedTransaction, participants))
val me = serviceHub.myInfo.legalIdentity
for ((stx, parties) in notarisedTxns) {
subFlow(BroadcastTransactionFlow(stx, parties + extraRecipients - me))
}
return notarisedTxns.map { it.first }
}
// TODO: API: Make some of these protected?
@Suspendable
private fun notariseAndRecord(stxnsAndParties: List<Pair<SignedTransaction, Set<Party>>>): List<Pair<SignedTransaction, Set<Party>>> {
return stxnsAndParties.map { pair ->
val stx = pair.first
val notarised = if (needsNotarySignature(stx)) {
val notarySig = subFlow(NotaryFlow.Client(stx))
stx + notarySig
} else {
stx
}
serviceHub.recordTransactions(listOf(notarised))
Pair(notarised, pair.second)
}
}
private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx)
@ -51,4 +90,38 @@ class FinalityFlow(val transaction: SignedTransaction,
val signers = stx.sigs.map { it.by }.toSet()
return !(notaryKey?.isFulfilledBy(signers) ?: false)
}
private fun lookupParties(ltxns: List<Pair<SignedTransaction, LedgerTransaction>>): List<Pair<SignedTransaction, Set<Party>>> {
return ltxns.map { pair ->
val (stx, ltx) = pair
// Calculate who is meant to see the results based on the participants involved.
val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail?
val parties = keys.mapNotNull { serviceHub.identityService.partyFromKey(it) }.toSet()
Pair(stx, parties)
}
}
private fun resolveDependenciesOf(signedTransactions: Iterable<SignedTransaction>): List<Pair<SignedTransaction, LedgerTransaction>> {
// Make sure the dependencies come before the dependers.
val sorted = ResolveTransactionsFlow.topologicalSort(signedTransactions.toList())
// Build a ServiceHub that consults the argument list as well as what's in local tx storage so uncommitted
// transactions can depend on each other.
val augmentedLookup = object : ServiceHub by serviceHub {
val hashToTx = sorted.associateBy { it.id }
override fun loadState(stateRef: StateRef): TransactionState<*> {
val provided: TransactionState<ContractState>? = hashToTx[stateRef.txhash]?.let { it.tx.outputs[stateRef.index] }
return provided ?: super.loadState(stateRef)
}
}
// Load and verify each transaction.
return sorted.map { stx ->
val notary = stx.tx.notary
// The notary signature is allowed to be missing but no others.
val wtx = if (notary != null) stx.verifySignatures(notary.owningKey) else stx.verifySignatures()
val ltx = wtx.toLedgerTransaction(augmentedLookup)
ltx.verify()
stx to ltx
}
}
}

View File

@ -23,7 +23,7 @@ object NotaryChangeFlow : AbstractStateReplacementFlow() {
class Instigator<out T : ContractState>(
originalState: StateAndRef<T>,
newNotary: Party,
progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Instigator<T, Party>(originalState, newNotary, progressTracker) {
progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> {
val state = originalState.state

View File

@ -5,8 +5,8 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.signWithECDSA
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessException
import net.corda.core.node.services.UniquenessProvider
@ -14,9 +14,9 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap
object NotaryFlow {
/**
* A flow to be used for obtaining a signature from a [NotaryService] ascertaining the transaction
* timestamp is correct and none of its inputs have been used in another completed transaction.

View File

@ -34,13 +34,16 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
companion object {
private fun dependencyIDs(wtx: WireTransaction) = wtx.inputs.map { it.txhash }.toSet()
private fun topologicalSort(transactions: Collection<SignedTransaction>): List<SignedTransaction> {
/**
* Topologically sorts the given transactions such that dependencies are listed before dependers. */
@JvmStatic
fun topologicalSort(transactions: Collection<SignedTransaction>): List<SignedTransaction> {
// Construct txhash -> dependent-txs map
val forwardGraph = HashMap<SecureHash, HashSet<SignedTransaction>>()
transactions.forEach { tx ->
tx.tx.inputs.forEach { input ->
transactions.forEach { stx ->
stx.tx.inputs.forEach { input ->
// Note that we use a LinkedHashSet here to make the traversal deterministic (as long as the input list is)
forwardGraph.getOrPut(input.txhash) { LinkedHashSet() }.add(tx)
forwardGraph.getOrPut(input.txhash) { LinkedHashSet() }.add(stx)
}
}

View File

@ -16,6 +16,7 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.trace
import net.corda.core.utilities.unwrap
import java.security.KeyPair
/**

View File

@ -0,0 +1,31 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.ALICE_PUBKEY
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* Tests for the version 2 dummy contract, to cover ensuring upgrade transactions are built correctly.
*/
class DummyContractV2Tests {
@Test
fun `upgrade from v1`() {
val contractUpgrade = DummyContractV2()
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY)
val v1Ref = StateRef(SecureHash.randomSHA256(), 0)
val v1StateAndRef = StateAndRef(v1State, v1Ref)
val (tx, signers) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef)
assertEquals(v1Ref, tx.inputs.single())
val expectedOutput = TransactionState(contractUpgrade.upgrade(v1State.data), DUMMY_NOTARY)
val actualOutput = tx.outputs.single()
assertEquals(expectedOutput, actualOutput)
val actualCommand = tx.commands.map { it.value }.single()
assertTrue((actualCommand as UpgradeCommand).upgradedContractClass == DummyContractV2::class.java)
}
}

View File

@ -3,26 +3,22 @@ package net.corda.core.crypto
import com.esotericsoftware.kryo.serializers.MapSerializer
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.`issued by`
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.*
import net.corda.core.transactions.*
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.testing.ALICE_PUBKEY
import net.corda.core.utilities.*
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.ledger
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlin.test.*
class PartialMerkleTreeTest {
val nodes = "abcdef"
val hashed = nodes.map { it.serialize().sha256() }
val root = SecureHash.parse("F6D8FB3720114F8D040D64F633B0D9178EB09A55AA7D62FAE1A070D1BF561051")
val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash
val merkleTree = MerkleTree.getMerkleTree(hashed)
val testLedger = ledger {
@ -33,22 +29,30 @@ class PartialMerkleTreeTest {
owner = MEGA_CORP_PUBKEY
)
}
output("dummy cash 1") {
Cash.State(
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = DUMMY_PUBKEY_1
)
}
}
transaction {
input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
timestamp(TEST_TX_TIME)
this.verifies()
}
}
val testTx = testLedger.interpreter.transactionsToVerify[0]
val txs = testLedger.interpreter.transactionsToVerify
val testTx = txs[0]
//Building full Merkle Tree tests.
// Building full Merkle Tree tests.
@Test
fun `building Merkle tree with 6 nodes - no rightmost nodes`() {
assertEquals(root, merkleTree.hash)
assertEquals(expectedRoot, merkleTree.hash)
}
@Test
@ -67,25 +71,70 @@ class PartialMerkleTreeTest {
fun `building Merkle tree odd number of nodes`() {
val odd = hashed.subList(0, 3)
val h1 = hashed[0].hashConcat(hashed[1])
val h2 = hashed[2].hashConcat(hashed[2])
val h2 = hashed[2].hashConcat(zeroHash)
val expected = h1.hashConcat(h2)
val mt = MerkleTree.getMerkleTree(odd)
assertEquals(mt.hash, expected)
}
@Test
fun `building Merkle tree for a transaction`() {
val filterFuns = FilterFuns(
filterCommands = { x -> ALICE_PUBKEY in x.signers },
filterOutputs = { true },
filterInputs = { true })
val mt = testTx.buildFilteredTransaction(filterFuns)
val d = WireTransaction.deserialize(testTx.serialized)
assertEquals(testTx.id, d.id)
assert(mt.verify(testTx.id))
fun `check full tree`() {
val h = SecureHash.randomSHA256()
val left = MerkleTree.Node(h, MerkleTree.Node(h, MerkleTree.Leaf(h), MerkleTree.Leaf(h)),
MerkleTree.Node(h, MerkleTree.Leaf(h), MerkleTree.Leaf(h)))
val right = MerkleTree.Node(h, MerkleTree.Leaf(h), MerkleTree.Leaf(h))
val tree = MerkleTree.Node(h, left, right)
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(tree, listOf(h)) }
PartialMerkleTree.build(right, listOf(h, h)) // Node and two leaves.
PartialMerkleTree.build(MerkleTree.Leaf(h), listOf(h)) // Just a leaf.
}
//Partial Merkle Tree building tests
@Test
fun `building Merkle tree for a transaction`() {
fun filtering(elem: Any): Boolean {
return when (elem) {
is StateRef -> true
is TransactionState<*> -> elem.data.participants[0].keys == DUMMY_PUBKEY_1.keys
is Command -> MEGA_CORP_PUBKEY in elem.signers
is Timestamp -> true
is CompositeKey -> elem == MEGA_CORP_PUBKEY
else -> false
}
}
val mt = testTx.buildFilteredTransaction(::filtering)
val leaves = mt.filteredLeaves
val d = WireTransaction.deserialize(testTx.serialized)
assertEquals(testTx.id, d.id)
assertEquals(1, leaves.commands.size)
assertEquals(1, leaves.outputs.size)
assertEquals(1, leaves.inputs.size)
assertEquals(1, leaves.mustSign.size)
assertEquals(0, leaves.attachments.size)
assertTrue(mt.filteredLeaves.timestamp != null)
assertEquals(null, mt.filteredLeaves.type)
assertEquals(null, mt.filteredLeaves.notary)
assert(mt.verify())
}
@Test
fun `same transactions with different notaries have different ids`() {
val wtx1 = makeSimpleCashWtx(DUMMY_NOTARY)
val wtx2 = makeSimpleCashWtx(MEGA_CORP)
assertNotEquals(wtx1.id, wtx2.id)
}
@Test
fun `nothing filtered`() {
val mt = testTx.buildFilteredTransaction( {false} )
assertTrue(mt.filteredLeaves.attachments.isEmpty())
assertTrue(mt.filteredLeaves.commands.isEmpty())
assertTrue(mt.filteredLeaves.inputs.isEmpty())
assertTrue(mt.filteredLeaves.outputs.isEmpty())
assertTrue(mt.filteredLeaves.timestamp == null)
assertFailsWith<MerkleTreeException> { mt.verify() }
}
// Partial Merkle Tree building tests
@Test
fun `build Partial Merkle Tree, only left nodes branch`() {
val inclHashes = listOf(hashed[3], hashed[5])
@ -137,7 +186,7 @@ class PartialMerkleTreeTest {
@Test
fun `verify Partial Merkle Tree - duplicate leaves failure`() {
val mt = MerkleTree.getMerkleTree(hashed.subList(0, 5)) //Odd number of leaves. Last one is duplicated.
val mt = MerkleTree.getMerkleTree(hashed.subList(0, 5)) // Odd number of leaves. Last one is duplicated.
val inclHashes = arrayListOf(hashed[3], hashed[4])
val pmt = PartialMerkleTree.build(mt, inclHashes)
inclHashes.add(hashed[4])
@ -162,11 +211,24 @@ class PartialMerkleTreeTest {
@Test
fun `hash map serialization`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
assert(serializedHash(hm1) == serializedHash(hm1.serialize().deserialize())) //It internally uses the ordered HashMap extension.
assert(serializedHash(hm1) == serializedHash(hm1.serialize().deserialize())) // It internally uses the ordered HashMap extension.
val kryo = extendKryoHash(createKryo())
assertTrue(kryo.getSerializer(HashMap::class.java) is OrderedSerializer)
assertTrue(kryo.getSerializer(LinkedHashMap::class.java) is MapSerializer)
val hm2 = hm1.serialize(kryo).deserialize(kryo)
assert(hm1.hashCode() == hm2.hashCode())
}
private fun makeSimpleCashWtx(notary: Party, timestamp: Timestamp? = null, attachments: List<SecureHash> = emptyList()): WireTransaction {
return WireTransaction(
inputs = testTx.inputs,
attachments = attachments,
outputs = testTx.outputs,
commands = testTx.commands,
notary = notary,
signers = listOf(MEGA_CORP_PUBKEY, DUMMY_PUBKEY_1),
type = TransactionType.General(),
timestamp = timestamp
)
}
}

View File

@ -0,0 +1,20 @@
package net.corda.core.crypto
import org.junit.Test
import java.math.BigInteger
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
class PartyTest {
@Test
fun `equality`() {
val key = entropyToKeyPair(BigInteger.valueOf(20170207L)).public.composite
val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public.composite
val anonymousParty = AnonymousParty(key)
val party = Party("test key", key)
assertEquals(party, anonymousParty)
assertEquals(anonymousParty, party)
assertNotEquals(AnonymousParty(differentKey), anonymousParty)
assertNotEquals(AnonymousParty(differentKey), party)
}
}

View File

@ -0,0 +1,127 @@
package net.corda.core.flows
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.utilities.Emoji
import net.corda.flows.CashIssueFlow
import net.corda.flows.ContractUpgradeFlow
import net.corda.flows.FinalityFlow
import net.corda.node.utilities.databaseTransaction
import net.corda.testing.node.MockNetwork
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.util.*
import java.util.concurrent.ExecutionException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class ContractUpgradeFlowTest {
lateinit var mockNet: MockNetwork
lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode
lateinit var notary: Party
@Before
fun setup() {
mockNet = MockNetwork()
val nodes = mockNet.createSomeNodes()
a = nodes.partyNodes[0]
b = nodes.partyNodes[1]
notary = nodes.notaryNode.info.notaryIdentity
mockNet.runNetwork()
}
@After
fun tearDown() {
mockNet.stopNodes()
}
@Test
fun `2 parties contract upgrade`() {
// Create dummy contract.
val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1))
val stx = twoPartyDummyContract.signWith(a.services.legalIdentityKey)
.signWith(b.services.legalIdentityKey)
.toSignedTransaction()
a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity)))
mockNet.runNetwork()
val atx = databaseTransaction(a.database) { a.services.storageService.validatedTransactions.getTransaction(stx.id) }
val btx = databaseTransaction(b.database) { b.services.storageService.validatedTransactions.getTransaction(stx.id) }
requireNotNull(atx)
requireNotNull(btx)
// The request is expected to be rejected because party B haven't authorise the upgrade yet.
val rejectedFuture = a.services.startFlow(ContractUpgradeFlow.Instigator<DummyContract.State, DummyContractV2.State>(atx!!.tx.outRef(0), DUMMY_V2_PROGRAM_ID.javaClass)).resultFuture
mockNet.runNetwork()
assertFailsWith(ExecutionException::class) { rejectedFuture.get() }
// Party B authorise the contract state upgrade.
b.services.vaultService.authoriseContractUpgrade(btx!!.tx.outRef<ContractState>(0), DUMMY_V2_PROGRAM_ID.javaClass)
// Party A initiates contract upgrade flow, expected to succeed this time.
val resultFuture = a.services.startFlow(ContractUpgradeFlow.Instigator<DummyContract.State, DummyContractV2.State>(atx.tx.outRef(0), DUMMY_V2_PROGRAM_ID.javaClass)).resultFuture
mockNet.runNetwork()
val result = resultFuture.get()
listOf(a, b).forEach {
val stx = databaseTransaction(a.database) { a.services.storageService.validatedTransactions.getTransaction(result.ref.txhash) }
requireNotNull(stx)
// Verify inputs.
val input = databaseTransaction(a.database) { a.services.storageService.validatedTransactions.getTransaction(stx!!.tx.inputs.single().txhash) }
requireNotNull(input)
assertTrue(input!!.tx.outputs.single().data is DummyContract.State)
// Verify outputs.
assertTrue(stx!!.tx.outputs.single().data is DummyContractV2.State)
}
}
@Test
fun `upgrade Cash to v2`() {
// Create some cash.
val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), a.info.legalIdentity, notary)).resultFuture
mockNet.runNetwork()
val stateAndRef = result.getOrThrow().tx.outRef<Cash.State>(0)
// Starts contract upgrade flow.
a.services.startFlow(ContractUpgradeFlow.Instigator<Cash.State, CashV2.State>(stateAndRef, CashV2().javaClass))
mockNet.runNetwork()
// Get contract state form the vault.
val state = databaseTransaction(a.database) { a.vault.currentVault.states }
assertTrue(state.single().state.data is CashV2.State, "Contract state is upgraded to the new version.")
assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (state.first().state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
assertEquals(listOf(a.info.legalIdentity.owningKey), (state.first().state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
}
class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
override val legacyContract = Cash()
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<CompositeKey>) : FungibleAsset<Currency> {
override val owner: CompositeKey = owners.first()
override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet()
override val contract = CashV2()
override val participants = owners
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: CompositeKey) = copy(amount = amount.copy(newAmount.quantity, amount.token), owners = listOf(newOwner))
override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: CompositeKey) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner)))
}
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))
override fun verify(tx: TransactionForContract) {}
// Dummy Cash contract for testing.
override val legalContractReference = SecureHash.sha256("")
}
}

View File

@ -146,7 +146,7 @@ class ResolveTransactionsFlowTest {
// DOCSTART 2
private fun makeTransactions(signFirstTX: Boolean = true, withAttachment: SecureHash? = null): Pair<SignedTransaction, SignedTransaction> {
// Make a chain of custody of dummy states and insert into node A.
val dummy1: SignedTransaction = DummyContract.generateInitial(MEGA_CORP.ref(1), 0, notary).let {
val dummy1: SignedTransaction = DummyContract.generateInitial(0, notary, MEGA_CORP.ref(1)).let {
if (withAttachment != null)
it.addAttachment(withAttachment)
if (signFirstTX)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -132,7 +132,7 @@ API reference: <a href="api/kotlin/corda/index.html">Kotlin</a>/ <a href="api/ja
<p class="caption"><span class="caption-text">CorDapps</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="creating-a-cordapp.html">CorDapp basics</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-cordapp.html">The CorDapp template</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-cordapp.html">The example CorDapp</a></li>
</ul>
<p class="caption"><span class="caption-text">The Corda node</span></p>
<ul>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 907 KiB

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -46,6 +46,14 @@ through to the server where the corresponding server-side observables are also u
a warning printed to the logs and the proxy will be closed for you. But don't rely on this, as garbage
collection is non-deterministic.
Futures
-------
A method can also return a ``ListenableFuture`` in its object graph and it will be treated in a similar manner to
observables, including needing to mark the RPC with the ``@RPCReturnsObservables`` annotation. Unlike for an observable,
once the single value (or an exception) has been received all server-side resources will be released automatically. Calling
the ``cancel`` method on the future will unsubscribe it from any future value and release any resources.
Versioning
----------

View File

@ -82,13 +82,16 @@ path to the node's base directory.
:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
:webAddress: The host and port on which the node is available for web operations.
:webAddress: The host and port on which the bundled webserver will listen if it is started.
.. note:: If HTTPS is enabled then the browser security checks will require that the accessing url host name is one
of either the machine name, fully qualified machine name, or server IP address to line up with the Subject Alternative
Names contained within the development certificates. This is addition to requiring the ``/config/dev/corda_dev_ca.cer``
root certificate be installed as a Trusted CA.
.. note:: The driver will not automatically create a webserver instance, but the Cordformation will. If this field
is present the web server will start.
:extraAdvertisedServiceIds: A list of ServiceType id strings to be advertised to the NetworkMapService and thus be available
when other nodes query the NetworkMapCache for supporting nodes. This can also include plugin services loaded from .jar
files in the plugins folder. Optionally, a custom advertised service name can be provided by appending it to the service

View File

@ -34,21 +34,16 @@ of the node internal subsystems.
extensions to be created, or registered at startup. In particular:
a. The ``webApis`` property is a list of JAX-RS annotated REST access
classes. These classes will be constructed by the embedded web server
and must have a single argument constructor taking a ``ServiceHub``
reference. This reference provides access to functions such as querying
for states through the ``VaultService`` interface, or access to the
``NetworkMapCache`` to identify services on remote nodes. The framework will
provide a database transaction in scope during the lifetime of the web
call, so full access to database data is valid. Unlike
``servicePlugins`` the ``webApis`` cannot register new protocols, or
initiate threads. (N.B. The intent is to move the Web support into a
separate helper process using the RPC mechanism to control access.)
classes. These classes will be constructed by the bundled web server
and must have a single argument constructor taking a ``CordaRPCOps``
reference. This will allow it to communicate with the node process
via the RPC interface. These web APIs will not be available if the
bundled web server is not started.
b. The ``staticServeDirs`` property maps static web content to virtual
paths and allows simple web demos to be distributed within the CorDapp
jars. (N.B. The intent is to move the Web support into a separate helper
process using the RPC mechanism to control access.)
jars. These static serving directories will not be available if the
bundled web server is not started.
c. The ``requiredFlows`` property is used to declare new protocols in
the plugin jar. Specifically the property must return a map with a key

View File

@ -12,10 +12,10 @@ App plugins
To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains
specific details of the implementation, but you can extend the server in the following ways:
1. Required flows: Specify which flows will be whitelisted for use in your web APIs.
1. Required flows: Specify which flows will be whitelisted for use in your RPC calls.
2. Service plugins: Register your services (see below).
3. Web APIs: You may register your own endpoints under /api/ of the built-in web server.
4. Static web endpoints: You may register your own static serving directories for serving web content.
3. Web APIs: You may register your own endpoints under /api/ of the bundled web server.
4. Static web endpoints: You may register your own static serving directories for serving web content from the web server.
5. Registering your additional classes used in RPC.
Services
@ -201,7 +201,7 @@ deployment.
To use this gradle plugin you must add a new task that is of the type ``net.corda.plugins.Cordform`` to your
build.gradle and then configure the nodes you wish to deploy with the Node and nodes configuration DSL.
This DSL is specified in the `JavaDoc <api/index.html>`_. An example of this is in the template-cordapp and below
This DSL is specified in the `JavaDoc <api/index.html>`_. An example of this is in the CorDapp template and below
is a three node example;
.. code-block:: text

View File

@ -89,7 +89,9 @@ Our flow has two parties (B and S for buyer and seller) and will proceed as foll
2. B sends to S a ``SignedTransaction`` that includes the state as input, B's cash as input, the state with the new
owner key as output, and any change cash as output. It contains a single signature from B but isn't valid because
it lacks a signature from S authorising movement of the asset.
3. S signs it and hands the now finalised ``SignedTransaction`` back to B.
3. S signs it and *finalises* the transaction. This means sending it to the notary, which checks the transaction for
validity, recording the transaction in the local vault, and then sending it back to B who also checks it and commits
the transaction to their local vault.
You can find the implementation of this flow in the file ``finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt``.
@ -98,8 +100,7 @@ represents an atomic asset swap.
Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
We start by defining a wrapper that namespaces the flow code, two functions to start either the buy or sell side
of the flow, and two classes that will contain the flow definition. We also pick what data will be used by
We start by defining two classes that will contain the flow definition. We also pick what data will be used by
each side.
.. note:: The code samples in this tutorial are only available in Kotlin, but you can use any JVM language to
@ -110,9 +111,8 @@ each side.
.. sourcecode:: kotlin
object TwoPartyTradeFlow {
class UnacceptablePriceException(val givenPrice: Amount<Currency>) : Exception("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
class UnacceptablePriceException(val givenPrice: Amount<Currency>) : FlowException("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : FlowException() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
}
@ -188,8 +188,6 @@ and try again.
.. note:: Java 9 is likely to remove this pre-marking requirement completely.
.. note:: Accessing the vault from inside an @Suspendable function (e.g. via ``serviceHub.vaultService``) can cause a serialisation error when the fiber suspends. Instead, vault access should be performed from a helper non-suspendable function, which you then call from the @Suspendable function. We are working to fix this.
Starting your flow
------------------
@ -241,55 +239,28 @@ Let's implement the ``Seller.call`` method. This will be run when the flow is in
.. container:: codeset
.. sourcecode:: kotlin
@Suspendable
override fun call(): SignedTransaction {
val partialTX: SignedTransaction = receiveAndCheckProposedTransaction()
val ourSignature: DigitalSignature.WithKey = computeOurSignature(partialTX)
val allPartySignedTx = partialTX + ourSignature
val notarySignature = getNotarySignature(allPartySignedTx)
val result: SignedTransaction = sendSignatures(allPartySignedTx, ourSignature, notarySignature)
return result
}
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 4
:end-before: DOCEND 4
:dedent: 4
Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it's
valid. The buyer has already attached their signature before sending it. Then we calculate and attach our own signature so that the transaction is
now signed by both the buyer and the seller. We then send this request to a notary to assert with another signature that the
timestamp in the transaction (if any) is valid and there are no double spends, and send back both
our signature and the notaries signature. Note we should not send to the notary until all other required signatures have been appended
as the notary may validate the signatures as well as verifying for itself the transactional integrity.
Finally, we hand back to the code that invoked the flow the finished transaction.
valid. The buyer has already attached their signature before sending it. Then we calculate and attach our own signature
so that the transaction is now signed by both the buyer and the seller. We then *finalise* this transaction by sending
it to a notary to assert (with another signature) that the timestamp in the transaction (if any) is valid and there are no
double spends. Finally, after the finalisation process is complete, we retrieve the now fully signed transaction from
local storage. It will have the same ID as the one we started with but more signatures.
Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
.. container:: codeset
.. sourcecode:: kotlin
@Suspendable
private fun receiveAndCheckProposedTransaction(): SignedTransaction {
// Make the first message we'll send to kick off the flow.
val myPublicKey = myKeyPair.public.composite
val hello = SellerTradeInfo(assetToSell, price, myPublicKey)
val maybeSTX = sendAndReceive<SignedTransaction>(otherSide, hello)
maybeSTX.unwrap {
// Check that the tx proposed by the buyer is valid.
val wtx: WireTransaction = it.verifySignatures(myPublicKey, notaryNode.notaryIdentity.owningKey)
logger.trace { "Received partially signed transaction: ${it.id}" }
// Download and check all the things that this transaction depends on and verify it is contract-valid,
// even though it is missing signatures.
subFlow(ResolveTransactionsFlow(wtx, otherParty))
if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price)
throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
return it
}
}
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 5
:end-before: DOCEND 5
:dedent: 4
Let's break this down. We fill out the initial flow message with the trade info, and then call ``sendAndReceive``.
This function takes a few arguments:
@ -333,8 +304,28 @@ Our "scrubbing" has three parts:
2. We resolve the transaction, which we will cover below.
3. We verify that the transaction is paying us the demanded price.
Sub-flows
---------
Exception handling
------------------
Flows can throw exceptions to prematurely terminate their execution. The flow framework gives special treatment to
``FlowException`` and its subtypes. These exceptions are treated as error responses of the flow and are propagated
to all counterparties it is communicating with. The receiving flows will throw the same exception the next time they do
a ``receive`` or ``sendAndReceive`` and thus end the flow session. If the receiver was invoked via ``subFlow`` (details below)
then the exception can be caught there enabling re-invocation of the sub-flow.
If the exception thrown by the erroring flow is not a ``FlowException`` it will still terminate but will not propagate to
the other counterparties. Instead they will be informed the flow has terminated and will themselves be terminated with a
generic exception.
.. note:: A future version will extend this to give the node administrator more control on what to do with such erroring
flows.
Throwing a ``FlowException`` enables a flow to reject a piece of data it has received back to the sender. This is typically
done in the ``unwrap`` method of the received ``UntrustworthyData``. In the above example the seller checks the price
and throws ``FlowException`` if it's invalid. It's then up to the buyer to either try again with a better price or give up.
Sub-flows and finalisation
--------------------------
Flows can be composed via nesting. Invoking a sub-flow looks similar to an ordinary function call:
@ -343,16 +334,29 @@ Flows can be composed via nesting. Invoking a sub-flow looks similar to an ordin
.. sourcecode:: kotlin
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
progressTracker.currentStep = NOTARY
return subFlow(NotaryFlow.Client(stx))
fun call() {
val unnotarisedTransaction = ...
subFlow(FinalityFlow(unnotarisedTransaction))
}
In this code snippet we are using the ``NotaryFlow.Client`` to request notarisation of the transaction.
.. sourcecode:: java
@Suspendable
public void call() throws FlowException {
SignedTransaction unnotarisedTransaction = ...
subFlow(new FinalityFlow(unnotarisedTransaction))
}
In this code snippet we are using the ``FinalityFlow`` to finish off the transaction. It will:
* Send the transaction to the chosen notary and, if necessary, satisfy the notary that the transaction is valid.
* Record the transaction in the local vault, if it is relevant (i.e. involves the owner of the node).
* Send the fully signed transaction to the other participants for recording as well.
We simply create the flow object via its constructor, and then pass it to the ``subFlow`` method which
returns the result of the flow's execution directly. Behind the scenes all this is doing is wiring up progress
tracking (discussed more below) and then running the objects ``call`` method. Because this little helper method can
be on the stack when network IO takes place, we mark it as ``@Suspendable``.
tracking (discussed more below) and then running the objects ``call`` method. Because the sub-flow might suspend,
we must mark the method that invokes it as suspendable.
Going back to the previous code snippet, we use a sub-flow called ``ResolveTransactionsFlow``. This is
responsible for downloading and checking all the dependencies of a transaction, which in Corda are always retrievable
@ -368,39 +372,11 @@ objects, but we don't need them here so we just ignore the return value.
After the dependencies, we check the proposed trading transaction for validity by running the contracts for that as
well (but having handled the fact that some signatures are missing ourselves).
Here's the rest of the code:
.. container:: codeset
.. sourcecode:: kotlin
open fun calculateOurSignature(partialTX: SignedTransaction) = myKeyPair.signWithECDSA(partialTX.id)
@Suspendable
private fun sendSignatures(allPartySignedTX: SignedTransaction, ourSignature: DigitalSignature.WithKey,
notarySignature: DigitalSignature.WithKey): SignedTransaction {
val fullySigned = allPartySignedTX + notarySignature
logger.trace { "Built finished transaction, sending back to secondary!" }
send(otherSide, SignaturesFromSeller(ourSignature, notarySignature))
return fullySigned
}
It's all pretty straightforward from now on. Here ``id`` is the secure hash representing the serialised
transaction, and we just use our private key to calculate a signature over it. As a reminder, in Corda signatures do
not cover other signatures: just the core of the transaction data.
In ``sendSignatures``, we take the two signatures we obtained and add them to the partial transaction we were sent.
There is an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
two signatures in a simple wrapper message class and send it back. The send won't block waiting for an acknowledgement,
but the underlying message queue software will retry delivery if the other side has gone away temporarily.
You can also see that every flow instance has a logger (using the SLF4J API) which you can use to log progress
messages.
.. warning:: This sample code is **not secure**. Other than not checking for all possible invalid constructions, if the
seller stops before sending the finalised transaction to the buyer, the seller is left with a valid transaction
but the buyer isn't, so they can't spend the asset they just purchased! This sort of thing will be fixed in a
future version of the code.
.. warning:: If the seller stops before sending the finalised transaction to the buyer, the seller is left with a
valid transaction but the buyer isn't, so they can't spend the asset they just purchased! This sort of thing is not
always a risk (as the seller may not gain anything from that sort of behaviour except a lawsuit), but if it is, a future
version of the platform will allow you to ask the notary to send you the transaction as well, in case your counterparty
does not. This is not the default because it reveals more private info to the notary.
Implementing the buyer
----------------------
@ -413,17 +389,16 @@ OK, let's do the same for the buyer side:
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
:dedent: 8
:dedent: 4
This code is longer but no more complicated. Here are some things to pay attention to:
1. We do some sanity checking on the received message to ensure we're being offered what we expected to be offered.
2. We create a cash spend in the normal way, by using ``VaultService.generateSpend``. See the vault documentation if this
part isn't clear.
2. We create a cash spend using ``VaultService.generateSpend``. You can read the vault documentation to learn more about this.
3. We access the *service hub* when we need it to access things that are transient and may change or be recreated
whilst a flow is suspended, things like the wallet or the network map.
4. Finally, we send the unfinished, invalid transaction to the seller so they can sign it. They are expected to send
back to us a ``SignaturesFromSeller``, which once we verify it, should be the final outcome of the trade.
4. We send the unfinished, invalid transaction to the seller so they can sign it and finalise it.
5. Finally, we wait for the finished transaction to arrive in our local storage and vault.
As you can see, the flow logic is straightforward and does not contain any callbacks or network glue code, despite
the fact that it takes minimal resources and can survive node restarts.
@ -450,10 +425,9 @@ A flow might declare some steps with code inside the flow class like this:
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCSTART 1
:end-before: DOCEND 2
:dedent: 4
.. sourcecode:: java
private final ProgressTracker progressTracker = new ProgressTracker(
@ -547,7 +521,7 @@ The flow framework is a key part of the platform and will be extended in major w
the features we have planned:
* Identity based addressing
* Exception propagation and management, with a "flow hospital" tool to manually provide solutions to unavoidable
* Exception management, with a "flow hospital" tool to manually provide solutions to unavoidable
problems (e.g. the other side doesn't know the trade)
* Being able to interact with internal apps and tools via RPC
* Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI.

View File

@ -4,9 +4,9 @@ Troubleshooting
Milestone releases
------------------
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 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``.
When developing on Corda, you should always check out the latest milestone (i.e. stable) branch instead. For example, to check out milestone 7, you'd run ``git checkout release-M7``.
Java issues
-----------
@ -19,7 +19,9 @@ Many users have faced issues when running versions of Java that are either outda
"Unresolved reference: javafx"
******************************
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``.
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.
If you have APT installed and OpenJFX is part of your Unix distribution's package list, you can do this by running ``sudo apt install openjfx``, and possibly ``sudo apt install libopenjfx-jav``. Other users will want to refer to the guide `here <https://wiki.openjdk.java.net/display/OpenJFX/Building+OpenJFX>`_, or to the list of Community Builds `here <https://wiki.openjdk.java.net/display/OpenJFX/Community+Builds>`_.
IDEA issues
---------------

View File

@ -69,13 +69,17 @@ The Corda platform source code is available here:
https://github.com/corda/corda.git
and a basic CorDapp that you can use as the basis for your own CorDapps is available here:
A CorDapp template that you can use as the basis for your own CorDapps is available here:
https://github.com/corda/cordapp-template.git
You can clone both of these repos to your local machine by running the command ``git clone [repo URL]``.
And a simple example CorDapp for you to explore basic concepts is available here:
By default, both repos will be on the ``master`` branch. However, this is an unstable development branch. You should check
https://github.com/corda/cordapp-tutorial.git
You can clone these repos to your local machine by running the command ``git clone [repo URL]``.
By default, these 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``.
Opening Corda/CorDapps in IDEA

View File

@ -2,33 +2,31 @@ Welcome to the Corda documentation!
===================================
.. warning:: This build of the docs is from the "|version|" branch, not a milestone release. It may not reflect the
current state of the code. `Read the docs for milestone release M7 <https://docs.corda.net/releases/release-M7.0/>`_.
current state of the code. `Read the docs for milestone release M8 <https://docs.corda.net/releases/release-M8.0/>`_.
`Corda <https://www.corda.net/>`_ is an open-source distributed ledger platform. The latest *milestone* (i.e. stable)
release is M7. The codebase is on `GitHub <https://github.com/corda>`_, and our community can be found on
`Corda <https://www.corda.net/>`_ is an open-source distributed ledger platform. The latest *milestone* (i.e. stable)
release is M8. The codebase is on `GitHub <https://github.com/corda>`_, and our community can be found on
`Slack <https://slack.corda.net/>`_ and in our `forum <https://discourse.corda.net/>`_.
If you're new to Corda, you should start by learning about its motivating vision and architecture. A good introduction
is the `Introduction to Corda webinar <https://vimeo.com/192757743/c2ec39c1e1>`_ and the `Introductory white paper`_. As
they become more familiar with Corda, readers with a technical background will also want to dive into the `Technical white paper`_,
If you're new to Corda, you should start by learning about its motivating vision and architecture. A good introduction
is the `Introduction to Corda webinar <https://vimeo.com/192757743/c2ec39c1e1>`_ and the `Introductory white paper`_. As
they become more familiar with Corda, readers with a technical background will also want to dive into the `Technical white paper`_,
which describes the platform's envisioned end-state.
Corda is designed so that developers can easily extend its functionality by writing CorDapps
(**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
Corda is designed so that developers can easily extend its functionality by writing CorDapps
(**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
`readme <https://github.com/corda/cordapp-template/blob/master/README.md>`_, or watch the
`Corda Developers Tutorial <https://vimeo.com/192797322/aab499b152>`_.
If, after running the demos, you're interested in writing your own CorDapps, you can use the
`CorDapp template <https://github.com/corda/cordapp-template>`_ as a base. A simple example CorDapp built upon the template is available `here <https://github.com/corda/cordapp-tutorial>`_, and a video primer on basic CorDapp structure is available `here <https://vimeo.com/192797322/aab499b152>`_.
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/tutorial-contract.html>`_
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/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/>`_.
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
platform itself. Find out more about `contributing to Corda <https://github.com/corda/corda/wiki/Corda's-Open-Source-Approach>`_.
.. _`Introductory white paper`: _static/corda-introductory-whitepaper.pdf

View File

@ -15,17 +15,17 @@ You can read more on the concept `here <https://en.wikipedia.org/wiki/Merkle_tre
Merkle trees in Corda
---------------------
Transactions are split into leaves, each of them contains either input, output, command or attachment. Other fields like
timestamp or signers are not used in the calculation.
Next, the Merkle tree is built in the normal way by hashing the concatenation
of nodes hashes below the current one together. Its visible on the example image below, where ``H`` denotes sha256 function,
"+" - concatenation.
Transactions are split into leaves, each of them contains either input, output, command or attachment. Additionally, in
transaction id calculation we use other fields of ``WireTransaction`` like timestamp, notary, type and signers.
Next, the Merkle tree is built in the normal way by hashing the concatenation of nodes hashes below the current one together.
Its visible on the example image below, where ``H`` denotes sha256 function, "+" - concatenation.
.. image:: resources/merkleTree.png
The transaction has one input state, one output and three commands. If a tree is not a full binary tree, the rightmost nodes are
duplicated in hash calculation (dotted lines).
The transaction has two input states, one of output, attachment and command each and timestamp. For brevity we didn't
include all leaves on the diagram (type, notary and signers are presented as one leaf labelled Rest - in reality
they are separate leaves). Notice that if a tree is not a full binary tree, leaves are padded to the nearest power
of 2 with zero hash (since finding a pre-image of sha256(x) == 0 is hard computational task) - marked light green above.
Finally, the hash of the root is the identifier of the transaction, it's also used for signing and verification of data integrity.
Every change in transaction on a leaf level will change its identifier.
@ -39,9 +39,11 @@ to that particular transaction.
.. image:: resources/partialMerkle.png
In the example above, the red node is the one holding data for signing Oracle service. Blue nodes' hashes form the Partial Merkle
Tree, dotted ones are not included. Having the command that should be in a red node place and branch we are able to calculate
root of this tree and compare it with original transaction identifier - we have a proof that this command belongs to this transaction.
In the example above, the node ``H(f)`` is the one holding command data for signing by Oracle service. Blue leaf ``H(g)`` is also
included since it's holding timestamp information. Nodes labelled ``Provided`` form the Partial Merkle Tree, black ones
are omitted. Having timestamp with the command that should be in a violet node place and branch we are able to calculate
root of this tree and compare it with original transaction identifier - we have a proof that this command and timestamp
belong to this transaction.
Example of usage
----------------
@ -50,36 +52,34 @@ Lets focus on a code example. We want to construct a transaction with command
:doc:`oracles`.
After construction of a partial transaction, with included ``Fix`` commands in it, we want to send it to the Oracle for checking
and signing. To do so we need to specify which parts of the transaction are going to be revealed. That can be done by constructing
filtering functions for inputs, outputs, attachments and commands separately. If a function is not provided by default none
of the elements from this group will be included in a Partial Merkle Tree.
filtering function over fields of ``WireTransaction`` of type ``(Any) -> Boolean``.
.. container:: codeset
.. sourcecode:: kotlin
val partialTx = ...
val partialTx = ...
val oracle: Party = ...
fun filterCommands(c: Command) = oracle.owningKey in c.signers && c.value is Fix
val filterFuns = FilterFuns(filterCommands = ::filterCommands)
fun filtering(elem: Any): Boolean {
return when (elem) {
is Command -> oracleParty.owningKey in elem.signers && elem.value is Fix
else -> false
}
}
Assuming that we already assembled partialTx with some commands and know the identity of Oracle service,
we pass filtering function over commands - ``filterCommands`` to ``FilterFuns``. It filters only
commands of type ``Fix`` as in IRSDemo example. Then we can construct ``FilteredTransaction``:
we construct filtering function over commands - ``filtering``. It performs type checking and filters only ``Fix`` commands
as in IRSDemo example. Then we can construct ``FilteredTransaction``:
.. container:: codeset
.. sourcecode:: kotlin
val wtx: WireTransaction = partialTx.toWireTransaction()
val ftx = FilteredTransaction.buildMerkleTransaction(wtx, filterFuns)
val ftx: FilteredTransaction = wtx.buildFilteredTransaction(filtering)
In the Oracle example this step takes place in ``RatesFixFlow``:
In the Oracle example this step takes place in ``RatesFixFlow`` by overriding ``filtering`` function, see: :ref:`filtering_ref`
.. container:: codeset
.. sourcecode:: kotlin
val flow = RatesFixFlow(partialTx, filterFuns, oracle, fixOf, "0.675".bd, "0.1".bd)
``FilteredTransaction`` holds ``filteredLeaves`` (data that we wanted to reveal) and Merkle branch for them.
@ -87,14 +87,21 @@ In the Oracle example this step takes place in ``RatesFixFlow``:
.. sourcecode:: kotlin
// Getting included commands, inputs, outputs, attachments.
// Direct accsess to included commands, inputs, outputs, attachments etc.
val cmds: List<Command> = ftx.filteredLeaves.commands
val ins: List<StateRef> = ftx.filteredLeaves.inputs
val outs: List<TransactionState<ContractState>> = ftx.filteredLeaves.outputs
val attchs: List<SecureHash> = ftx.filteredLeaves.attachments
val timestamp: Timestamp? = ftx.filteredLeaves.timestamp
...
.. literalinclude:: ../../samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
If you want to verify obtained ``FilteredTransaction`` all you need is the root hash of the full transaction:
Above code snippet is taken from ``NodeInterestRates.kt`` file and implements a signing part of an Oracle.
You can check only leaves using ``leaves.checkWithFun { check(it) }`` and then verify obtained ``FilteredTransaction``
to see if data from ``PartialMerkleTree`` belongs to ``WireTransaction`` with provided ``id``. All you need is the root hash
of the full transaction:
.. container:: codeset
@ -104,6 +111,13 @@ If you want to verify obtained ``FilteredTransaction`` all you need is the root
throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.")
}
Or combine the two steps together:
.. container:: codeset
.. sourcecode:: kotlin
ftx.verifyWithFunction(merkleRoot, ::check)
.. note:: The way the ``FilteredTransaction`` is constructed ensures that after signing of the root hash it's impossible to add or remove
leaves. However, it can happen that having transaction with multiple commands one party reveals only subset of them to the Oracle.

View File

@ -1,8 +1,9 @@
Node administration
===================
When a node is running, it exposes an embedded database server, an embedded web server that lets you monitor it,
you can upload and download attachments, access a REST API and so on.
When a node is running, it exposes an RPC interface that lets you monitor it,
you can upload and download attachments, access a REST API and so on. A bundled
Jetty web server exposes the same interface over HTTP.
Logging
-------

View File

@ -63,11 +63,11 @@ The Demo Nodes can be started in one of two modes:
.. note:: 5 Corda nodes will be created on the following port on localhost by default.
* Notary -> 20002
* Alice -> 20004
* Bob -> 20006
* Notary -> 20001
* Alice -> 20003
* Bob -> 20005
* UK Bank Plc -> 20008 (*Issuer node*)
* USA Bank Corp -> 20010 (*Issuer node*)
* USA Bank Corp -> 20009 (*Issuer node*)
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.

View File

@ -346,3 +346,11 @@ external legacy systems by insertion of unpacked data into existing
tables. To enable these features the contract state must implement the
``QueryableState`` interface to define the mappings.
Node Web Server
---------------
A web server comes bundled with the node by default, but is not started
automatically. This web server exposes both RPC backed API calls and
static content serving. The web server is not automatically started,
you must explicitly start it in the node driver or define a web port
in your `Cordformation`_ configuration.

View File

@ -239,8 +239,11 @@ those for ``NodeInterestRates.Oracle``.
:start-after: DOCSTART 1
:end-before: DOCEND 1
You'll note that the ``FixSignFlow`` requires a ``FilterFuns`` instance with the appropriate filter to include only
the ``Fix`` commands. You can find a further explanation of this in :doc:`merkle-trees`.
You'll note that the ``FixSignFlow`` requires a ``FilterTransaction`` instance which includes only ``Fix`` commands.
You can find a further explanation of this in :doc:`merkle-trees`. Below you will see how to build such transaction with
hidden fields.
.. _filtering_ref:
Using an oracle
---------------
@ -260,8 +263,9 @@ As you can see, this:
2. Does some quick validation.
3. Adds the command to the transaction containing the fact to be signed for by the oracle.
4. Calls an extension point that allows clients to generate output states based on the fact from the oracle.
5. Requests the signature from the oracle using the client sub-flow for signing from above.
6. Adds the signature returned from the oracle.
5. Builds filtered transaction based on filtering function extended from ``RatesFixFlow``.
6. Requests the signature from the oracle using the client sub-flow for signing from above.
7. Adds the signature returned from the oracle.
Here's an example of it in action from ``FixingFlow.Fixer``.
@ -269,3 +273,8 @@ Here's an example of it in action from ``FixingFlow.Fixer``.
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
.. note::
When overriding be careful when making the sub-class an anonymous or inner class (object declarations in Kotlin),
because that kind of classes can access variables from the enclosing scope and cause serialization problems when
checkpointed.

View File

@ -6,10 +6,33 @@ Here are brief summaries of what's changed between each snapshot release.
Milestone 8
-----------
* API:
* Node memory usage and performance improvements, demo nodes now only require 200 MB heap space to run.
* ``Party`` equality is now based on the owning key, rather than the owning key and name. This is important for
party anonymisation to work, as each key must identify exactly one party.
* The Corda node no longer runs an internal web server, it's now run in a separate process. Driver and Cordformation have
been updated to reflect this change.
Existing CorDapps should be updated with additional calls to the new ``startWebserver()`` interface in their Driver logic (if they use the driver e.g. in integration tests).
See the IRS demo for an example.
* Data model: ``Party`` equality is now based on the owning key, rather than the owning key and name. This is important for
party anonymisation to work, as each key must identify exactly one party.
* Contracts: created new composite clauses called ``AllOf``, ``AnyOf`` and ``FirstOf`` to replace ``AllComposition``, ``AnyComposition``
and ``FirstComposition``, as this is significantly clearer in intent. ``AnyOf`` also enforces that at least one subclause
must match, whereas ``AnyComposition`` would accept no matches.
* Explorer: the user can now configure certificate path and keystore/truststore password on the login screen.
* Documentation:
* Key Concepts section revamped with new structure and content.
* Added more details to :doc:`getting-set-up` page.
* Flow framework: improved exception handling with the introduction of ``FlowException``. If this or a subtype is thrown
inside a flow it will propagate to all counterparty flows and subsequently be thrown by them as well. Existing flows such as
``NotaryFlow.Client/Service`` and others have been modified to throw a ``FlowException`` (in this particular case a
``NotaryException``) instead of sending back error responses.
* Notary flow: provide complete details of underlying error when contract validation fails.
Milestone 7
-----------

View File

@ -45,7 +45,8 @@ The most important fields regarding network configuration are:
* ``artemisAddress``: This specifies a host and port. Note that the address bound will **NOT** be ``my-corda-node``,
but rather ``::`` (all addresses on all interfaces). The hostname specified is the hostname *that must be externally
resolvable by other nodes in the network*. In the above configuration this is the resolvable name of a machine in a vpn.
* ``webAddress``: The address the webserver should bind. Note that the port should be distinct from that of ``artemisAddress``.
* ``webAddress``: The address the webserver should bind. Note that the port should be distinct from that of ``artemisAddress``
if they are on the same machine.
* ``networkMapService``: Details of the node running the network map service. If it's this node that's running the service
then this field must not be specified.

View File

@ -4,14 +4,18 @@
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
The CorDapp template
====================
The example CorDapp
===================
This guide covers how to get started with the `cordapp-template`. Please note there are two Corda repositories:
.. note:: The example CorDapp was previously hosted in the `cordapp-template <https://github.com/corda/cordapp-template>`_ repository. It has now been
moved into a new `cordapp-tutorial <https://github.com/corda/cordapp-tutorial>`_ repository. Going forward, the cordapp-template repo will contain an
actual CorDapp template, which should be used as the basis for your CorDapps going forward.
* ``corda`` which contains the core platform code and sample CorDapps.
* ``cordapp-template`` which contains a template CorDapp you can use to bootstrap your own CorDapps. It is the subject
of this tutorial and should help you understand the basics of building a CorDapp.
This guide covers how to get started with the example CorDapp. Please note there are several Corda repositories:
* `corda <https://github.com/corda/corda>`_ which contains the core platform code and sample CorDapps.
* `cordapp-tutorial <https://github.com/corda/cordapp-tutorial>`_ which contains an example CorDapp you can use to bootstrap your own CorDapps. It is the subject of this tutorial and should help you understand the basics of building a CorDapp.
* `cordapp-template <https://github.com/corda/cordapp-template>`_ which contains a bare-bones template on which to build your own CorDapps.
We recommend you read the non-technical white paper and technical white paper before you get started with Corda:
@ -25,30 +29,29 @@ We recommend you read the non-technical white paper and technical white paper be
Background
----------
The Example CorDapp implements a basic scenario where a buyer wishes to submit purchase orders to a seller. The scenario
The Example CorDapp implements a basic scenario where one party wishes to send an IOU to another party. The scenario
defines four nodes:
* **Controller** which hosts the network map service and validating notary service.
* **NodeA** who is the buyer.
* **NodeB** who is the seller.
* **NodeC** an unrelated third party.
* **Controller**, which hosts the network map service and validating notary service
* **NodeA**
* **NodeB**
* **NodeC**
NodeA can generate purchase orders for lists and quantities of items and associated metadata such as delivery address
and delivery date. The flows used to facilitate the agreement process always results in an agreement with the seller as
long as the purchase order meets the contract constraints which are defined in ``PurchaseOrderContract.kt``.
The nodes can generate IOUs and send them to other nodes. The flows used to facilitate the agreement process always results in
an agreement with the recipient as long as the IOU meets the contract constraints which are defined in ``IOUContract.kt``.
All agreed purchase orders between NodeA and NodeB become "shared facts" between NodeA and NodeB. But note that NodeC
won't see any of these transactions or have copies of any of the resulting ``PurchaseOrderState`` objects. This is
All agreed IOUs between NodeA and NodeB become "shared facts" between NodeA and NodeB. But note that NodeC
won't see any of these transactions or have copies of any of the resulting ``IOUState`` objects. This is
because data is only propagated on a need-to-know basis.
Getting started
---------------
There are two ways to get started with the CorDapp template. You can either work from a milestone release of Corda or a
There are two ways to get started with the example CorDapp. You can either work from a milestone release of Corda or a
SNAPSHOT release of Corda.
**Using a monthly Corda milestone release.** If you wish to develop your CorDapp using the most recent milestone release
then you can get started simply by cloning the ``cordapp-template`` repository. Gradle will grab all the required dependencies
then you can get started simply by cloning the ``cordapp-tutorial`` repository. Gradle will grab all the required dependencies
for you from our `public Maven repository <https://bintray.com/r3/corda>`_.
**Using a Corda SNAPSHOT build.** Alternatively, if you wish to work from the master branch of the Corda repo which contains
@ -69,21 +72,21 @@ The process for developing your CorDapp from a milestone release is the most sim
approach.
We publish all our milestone releases to a public Maven repository on a monthly basis. As such, Gradle will automatically
grab the appropriately versioned (specified in the ``cordapp-template``'s ``build.gradle`` file) dependencies for you from Maven.
All you have to do is check out the release tag of the template version you wish to use.
grab the appropriately versioned (specified in the ``cordapp-tutorial``'s ``build.gradle`` file) dependencies for you from Maven.
All you have to do is check out the release tag of the tutorial version you wish to use.
By default, the ``master`` branch of the ``cordapp-template`` points to a SNAPSHOT release of Corda, this is because it is
By default, the ``master`` branch of the ``cordapp-tutorial`` points to a SNAPSHOT release of Corda, this is because it is
being constantly updated to reflect the changes in the master branch of the `corda` repository.
.. note:: If you wish to use a SNAPSHOT release then follow the instructions below: `Using a SNAPSHOT release`_.
To clone the ``cordapp-template`` repository, use the following command:
To clone the ``cordapp-tutorial`` repository, use the following command:
``git clone https://github.com/corda/cordapp-template``
``git clone https://github.com/corda/cordapp-tutorial``
Now change directories to the freshly cloned repo:
``cd cordapp-template``
``cd cordapp-tutorial``
To enumerate all the tagged releases. Use:
@ -95,7 +98,7 @@ To checkout a specific tag, use:
where ``local_branch_name`` is a name of your choice and ``tag_name`` is the name of the tag you wish to checkout.
Gradle will handle all the dependencies for you. Now you are now ready to get started `building the CorDapp Template`_.
Gradle will handle all the dependencies for you. Now you are now ready to get started `building the example CorDapp`_.
Using a SNAPSHOT release
~~~~~~~~~~~~~~~~~~~~~~~~
@ -145,7 +148,7 @@ where ``local_branch_name`` is a name of your choice and ``remote_branch_name``
to checkout.
.. note:: When working with ``master`` you will have access to the most up-to-date feature set. However you will be
potentially sacrificing stability. We will endeavour to keep the ``master`` branch of the ``cordapp-template`` repo in sync
potentially sacrificing stability. We will endeavour to keep the ``master`` branch of the ``cordapp-tutorial`` repo in sync
with the ``master`` branch of ``corda`` repo. A milestone tagged release would be more stable for CorDapp development.
The next step is to publish the Corda JARs to your local Maven repository. By default the Maven local repository can be
@ -198,13 +201,13 @@ and to check out an open feature branch, use:
Once you have published the Corda JARs to your local Maven repository, you are ready to get started building your
CorDapp using the latest Corda features.
Opening the CorDapp Template with IntelliJ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Opening the example CorDapp with IntelliJ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For those familiar with IntelliJ, you can skip this section.
As noted in the getting started guide, we recommend using the IntelliJ IDE. Assuming you have already downloaded and
installed IntelliJ, lets now open the CorDapp Template with IntelliJ.
installed IntelliJ, lets now open the example CorDapp with IntelliJ.
**For those completely new to IntelliJ**
@ -213,7 +216,7 @@ Firstly, load up IntelliJ. A dialogue will appear:
.. image:: resources/intellij-welcome.png
:width: 400
Click open, then navigate to the folder where you cloned the ``cordapp-template`` and click OK.
Click open, then navigate to the folder where you cloned the ``cordapp-tutorial`` and click OK.
Next, IntelliJ will show a bunch of pop-up windows. One of which requires our attention:
@ -227,14 +230,14 @@ dialogue, simply close and re-open IntelliJ again to see it again.
**Alternative approach**
Alternatively, one can instruct IntelliJ to create a new project through cloning a repository. From the IntelliJ welcome
dialogue (shown above), opt to 'check out from version control', then select git and enter the git URL for the CorDapp template
(https://github.com/corda/cordapp-template). You'll then need to import the Gradle project when prompted, as explained above.
dialogue (shown above), opt to 'check out from version control', then select git and enter the git URL for the example CorDapp
(https://github.com/corda/cordapp-tutorial). You'll then need to import the Gradle project when prompted, as explained above.
**If you already have IntelliJ open**
From the ``File`` menu, navigate to ``Open ...`` and then navigate to the directory where you cloned the ``cordapp-template``.
From the ``File`` menu, navigate to ``Open ...`` and then navigate to the directory where you cloned the ``cordapp-tutorial``.
Alternatively, if you wish to clone from github directly then navigate to ``File > New > Project from existing sources ...``
and enter the URL to the CorDapp Template (specified above). When instructed, be sure to import the Gradle project when prompted.
and enter the URL to the example CorDapp (specified above). When instructed, be sure to import the Gradle project when prompted.
**The Gradle plugin**
@ -242,30 +245,30 @@ IntelliJ can be used to run Gradle tasks through the Gradle plugin which can be
All the Gradle projects are listed in the window on the right hand side of the IDE. Click on a project, then 'tasks' to
see all available Gradle tasks.
* For the CorDapp Template repo there will only be one Gradle project listed.
* For the example CorDapp repo there will only be one Gradle project listed.
* For the Corda repo there will be many project listed, the root project ``corda`` and associated sub-projects: ``core``,
``finance``, ``node``, etc.
.. note:: It's worth noting that when you change branch in the CorDapp template, the ``corda_version`` will change to
.. note:: It's worth noting that when you change branch in the example CorDapp, the ``corda_version`` will change to
reflect the version of the branch you are working from.
To execute a task, double click it. The output will be shown in a console window.
Building the CorDapp template
-----------------------------
Building the example CorDapp
----------------------------
**From the command line**
Firstly, return to your terminal window used above and make sure you are in the ``cordapp-template`` directory.
Firstly, return to your terminal window used above and make sure you are in the ``cordapp-tutorial`` directory.
To build the CorDapp template use the following command:
To build the example CorDapp use the following command:
Unix/Mac OSX: ``./gradlew deployNodes``
Windows: ``gradlew.bat deployNodes``
This build process will build the example CorDapp defined in the CorDapp template source. CorDapps can be written in
any language targeting the JVM. In our case, we've provided the template source in both Kotlin (``/kotlin/src``) and
This build process will build the example CorDapp defined in the example CorDapp source. CorDapps can be written in
any language targeting the JVM. In our case, we've provided the example source in both Kotlin (``/kotlin/src``) and
Java (``/java/src``) Since both sets of source files are functionally identical, we will refer to the Kotlin build
throughout the documentation.
@ -276,7 +279,7 @@ The ``deployNodes`` Gradle task allows you easily create a formation of Corda no
we are creating ``four`` nodes.
After the building process has finished to see the newly built nodes, you can navigate to the ``kotlin/build/nodes`` folder
located in the ``cordapp-template`` root directory. You can ignore the other folders in ``/build`` for now. The ``nodes``
located in the ``cordapp-tutorial`` root directory. You can ignore the other folders in ``/build`` for now. The ``nodes``
folder has the following structure:
.. sourcecode:: none
@ -317,10 +320,10 @@ Open the Gradle window by selecting ``View > Tool windows > Gradle`` from the ma
open on the right hand side of the IDE. Expand `tasks` and then expand `other`. Double click on `deployNodes`. Gradle will
start the build process and output progress to a console window in the IDE.
Running the CorDapp template
----------------------------
Running the example CorDapp
---------------------------
Running the CorDapp template from the command line
Running the example CorDapp from the command line
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To run the sample CorDapp navigate to the ``kotlin/build/nodes`` folder and execute the ``runnodes`` shell script with:
@ -342,7 +345,7 @@ message and some pertinent config information, see below:
--- DEVELOPER SNAPSHOT ------------------------------------------------------------
Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-template/kotlin/build/nodes/nodea/logs
Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-tutorial/kotlin/build/nodes/nodea/logs
Database connection URL is : jdbc:h2:tcp://10.18.0.196:50661/node
Node listening on address : localhost:10004
Loaded plugins : com.example.plugin.ExamplePlugin
@ -416,14 +419,14 @@ To stop the nodes, press the red "stop" button at the top right-hand side of the
The node driver can also be used to as a basis for `debugging your CorDapp`_
Interacting with the CorDapp template
-------------------------------------
Interacting with the example CorDapp
------------------------------------
Via HTTP
~~~~~~~~
The CorDapp defines a few HTTP API end-points and also serves some static web content. The end-points allow you to
list purchase orders and add purchase orders.
list IOUs and add IOUs.
The nodes can be found using the following port numbers, defined in build.gradle and the respective node.conf file for
each node found in `kotlin/build/nodes/NodeX`` etc:
@ -440,32 +443,32 @@ served are as follows:
* ``/api/example/me``
* ``/api/example/peers``
* ``/api/example/purchase-orders``
* ``/api/example/{COUNTERPARTY}/create-purchase-order``
* ``/api/example/ious``
* ``/api/example/{COUNTERPARTY}/create-iou``
The static web content is served from ``/web/example``.
A purchase order can be created via accessing the ``api/example/create-purchase-order`` end-point directly or through the
An IOU can be created via accessing the ``api/example/create-iou`` end-point directly or through the
the web form hosted at ``/web/example``.
.. warning:: **The content in ``web/example`` is only available for demonstration purposes and does not implement any
anti-XSS, anti-XSRF or any other security techniques. Do not copy such code directly into products meant for production use.**
**Submitting a purchase order via HTTP API:**
**Submitting an IOU via HTTP API:**
To create a purchase order from NodeA to NodeB, use:
To create an IOU from NodeA to NodeB, use:
.. sourcecode:: bash
echo '{"orderNumber": "1","deliveryDate": "2018-09-15","deliveryAddress": {"city": "London","country": "UK"},"items" : [{"name": "widget","amount": "3"},{"name": "thing","amount": "4"}]}' | cURL -T - -H 'Content-Type: application/json' http://localhost:10005/api/example/NodeB/create-purchase-order
echo '{"value": "1"}' | cURL -T - -H 'Content-Type: application/json' http://localhost:10005/api/example/NodeB/create-iou
Note the port number ``10005`` (NodeA) and NodeB referenced in the API end-point path. This command instructs NodeA to
create and send a purchase order to NodeB. Upon verification and completion of the process, both nodes (but not NodeC) will
have a signed, notarised copy of the purchase order.
create and send an IOU to NodeB. Upon verification and completion of the process, both nodes (but not NodeC) will
have a signed, notarised copy of the IOU.
**Submitting a purchase order via web/example:**
**Submitting an IOU via web/example:**
Navigate to the "create purchase order" button at the top left of the page, enter in the purchase order details e.g.
Navigate to the "create IOU" button at the top left of the page, and enter the IOU details - e.g.
.. sourcecode:: none
@ -478,20 +481,22 @@ Navigate to the "create purchase order" button at the top left of the page, ente
Item amount: 5
and click submit (note you can add additional item types and amounts). Upon pressing submit, the modal dialogue should close.
To check what validation is performed over the purchase order data, have a look at the ``PurchaseOrderContract.Place`` class in
``PurchaseOrderContract.kt`` which defines the following contract constraints (among others not included here):
To check what validation is performed over the IOU data, have a look at the ``IOUContract.Create`` class in
``IOUContract.kt`` which defines the following contract constraints (among others not included here):
.. sourcecode:: kotlin
// Purchase order specific constraints.
"We only deliver to the UK." by (out.po.deliveryAddress.country == "UK")
"You must order at least one type of item." by (out.po.items.size > 0)
"You cannot order zero or negative amounts of an item." by (out.po.items.map(Item::amount).all { it > 0 })
"You can only order up to 10 items at a time." by (out.po.items.map(Item::amount).sum() <= 10)
val time = tx.timestamp?.midpoint
"The delivery date must be in the future." by (out.po.deliveryDate.toInstant() > time)
// Generic constraints around the IOU transaction.
"No inputs should be consumed when issuing an IOU." by (tx.inputs.isEmpty())
"Only one output state should be created." by (tx.outputs.size == 1)
val out = tx.outputs.single() as IOUState
"The sender and the recipient cannot be the same entity." by (out.sender != out.recipient)
"All of the participants must be signers." by (command.signers.containsAll(out.participants))
**Once a purchase order has been submitted:**
// IOU-specific constraints.
"The IOU's value must be non-negative." by (out.iou.value > 0)
**Once an IOU has been submitted:**
Inspect the terminal windows for the nodes. Assume all of the above contract constraints are met, you should see some
activity in the terminal windows for NodeA and NodeB (note: the green ticks are only visible on unix/mac):
@ -500,29 +505,25 @@ activity in the terminal windows for NodeA and NodeB (note: the green ticks are
.. sourcecode:: none
✅ Constructing proposed purchase order.
✅ Sending purchase order to seller for review.
✅ Received partially signed transaction from seller.
✅ Verifying signatures and contract constraints.
✅ Signing transaction with our private key.
✅ Obtaining notary signature.
✅ Requesting signature by Notary service
✅ Validating response from Notary service
✅ Recording transaction in vault.
✅ Sending fully signed transaction to seller.
✅ Done
✅ Generating transaction based on new IOU.
✅ Verifying contract constraints.
✅ Signing transaction with our private key.
✅ Sending proposed transaction to recipient for review.
✅ Done
*NodeB:*
.. sourcecode:: none
✅ Receiving proposed purchase order from buyer.
✅ Generating transaction based on proposed purchase order.
✅ Signing proposed transaction with our private key.
✅ Sending partially signed transaction to buyer and wait for a response.
✅ Verifying signatures and contract constraints.
✅ Recording transaction in vault.
✅ Done
✅ Receiving proposed transaction from sender.
✅ Verifying signatures and contract constraints.
✅ Signing proposed transaction with our private key.
✅ Obtaining notary signature and recording transaction.
✅ Requesting signature by notary service
✅ Requesting signature by Notary service
✅ Validating response from Notary service
✅ Broadcasting transaction to participants
✅ Done
*NodeC:*
@ -530,12 +531,12 @@ activity in the terminal windows for NodeA and NodeB (note: the green ticks are
You shouldn't see any activity.
Next you can view the newly created purchase order by accessing the vault of NodeA or NodeB:
Next you can view the newly created IOU by accessing the vault of NodeA or NodeB:
*Via the HTTP API:*
For NodeA. navigate to http://localhost:10005/api/example/purchase-orders. For NodeB,
navigate to http://localhost:10007/api/example/purchase-orders.
For NodeA. navigate to http://localhost:10005/api/example/ious. For NodeB,
navigate to http://localhost:10007/api/example/ious.
*Via web/example:*
@ -575,9 +576,9 @@ Using the Example RPC client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``/src/main/kotlin/com/example/client/ExampleClientRPC.kt`` file is a simple utility which uses the client RPC library
to connect to a node and log the 'placed' purchase orders. It will log any existing purchase orders and listen for any future
purchase orders. If you haven't placed any purchase orders when you connect to one of the Nodes via RPC then the client will log
and future purchase orders which are agreed.
to connect to a node and log the created IOUs. It will log any existing IOUs and listen for any future
IOUs. If you haven't created any IOUs when you connect to one of the Nodes via RPC then the client will log
and future IOUs which are agreed.
To build the client use the following gradle task:
@ -600,17 +601,17 @@ application see:
* :doc:`Client RPC documentation <clientrpc>`
* :doc:`Client RPC tutorial <tutorial-clientrpc-api>`
Extending the CorDapp template
------------------------------
Extending the example CorDapp
-----------------------------
CorDapp-template project structure
cordapp-tutorial project structure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The CorDapp template has the following directory structure:
The example CorDapp has the following directory structure:
.. sourcecode:: none
. cordapp-template
. cordapp-tutorial
├── README.md
├── LICENSE
├── build.gradle
@ -624,7 +625,7 @@ The CorDapp template has the following directory structure:
├── lib
│   ├── ...
├── settings.gradle
├── kotlin
├── kotlin-source
│ └── src
│ ├── main
   │   ├── kotlin
@ -636,10 +637,10 @@ The CorDapp template has the following directory structure:
   │   │   ├── client
   │   │   │   └── ExampleClientRPC.kt
   │   │   ├── contract
   │   │   │   ├── PurchaseOrderContract.kt
   │   │   │   └── PurchaseOrderState.kt
   │   │   │   ├── IOUContract.kt
   │   │   │   └── IOUState.kt
   │   │   ├── model
   │   │   │   └── PurchaseOrder.kt
   │   │   │   └── IOU.kt
   │   │   ├── plugin
   │   │   │   └── ExamplePlugin.kt
   │   │   └── flow
@ -666,7 +667,7 @@ The CorDapp template has the following directory structure:
│ │   └── example
│ │   └── ExampleTest.kt
   └── resources
└── java
└── java-source
└── src
├── main
   │   ├── java
@ -678,10 +679,10 @@ The CorDapp template has the following directory structure:
   │   │   ├── client
   │   │   │   └── ExampleClientRPC.java
   │   │   ├── contract
   │   │   │   ├── PurchaseOrderContract.java
   │   │   │   └── PurchaseOrderState.java
   │   │   │   ├── IOUContract.java
   │   │   │   └── IOUState.java
   │   │   ├── model
   │   │   │   └── PurchaseOrder.java
   │   │   │   └── IOU.java
   │   │   ├── plugin
   │   │   │   └── ExamplePlugin.java
   │   │   └── flow
@ -740,8 +741,8 @@ things.
If you are working from a Corda SNAPSHOT release which you have publish to Maven local then ensure that
``corda_version`` is the same as the version of the Corda core modules you published to Maven local. If not then change the
``kotlin_version`` property. Also, if you are working from a previous milestone release, then be sure to ``git checkout``
the correct version of the CorDapp template from the ``cordapp-template`` repo.
``kotlin_version`` property. Also, if you are working from a previous cordapp-tutorial milestone release, then be sure to ``git checkout``
the correct version of the example CorDapp from the ``cordapp-tutorial`` repo.
.. sourcecode:: groovy
@ -824,7 +825,7 @@ like to deploy for testing. See further details below:
You can add any number of nodes, with any number of services / CorDapps by editing the templates in ``deployNodes``. The
only requirement is that you must specify a node to run as the network map service and one as the notary service.
.. note:: CorDapps in the current cordapp-template project are automatically registered with all nodes defined in
.. note:: CorDapps in the current cordapp-tutorial project are automatically registered with all nodes defined in
``deployNodes``, although we expect this to change in the near future.
.. warning:: Make sure that there are no port clashes!
@ -834,7 +835,7 @@ When you are finished editing your *CordFormation* the changes will be reflected
Service Provider Configuration File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are building a CorDapp from scratch or adding a new CorDapp to the CorDapp-template project then you must provide
If you are building a CorDapp from scratch or adding a new CorDapp to the cordapp-tutorial project then you must provide
a reference to your sub-class of ``CordaPluginRegistry`` in the provider-configuration file in located in the ``resources/META-INF/services`` directory.
Re-Deploying Your Nodes Locally
@ -881,8 +882,8 @@ controller node is running and to its legal name (e.g. `networkMapService.addres
`networkMapService.legalName=controller`) (please note that the controller will not have the `networkMapService` config)
Each machine should now run its nodes using `runnodes` or `runnodes.bat`
files. Once they are up and running, the nodes should be able to place
purchase orders among themselves in the same way as when they were running on
files. Once they are up and running, the nodes should be able to create
IOUs among themselves in the same way as when they were running on
the same machine.
Debugging your CorDapp

View File

@ -2,10 +2,10 @@
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_102) on Thu Jan 26 16:01:09 GMT 2017 -->
<!-- Generated by javadoc (1.8.0_112) on Tue Feb 07 15:56:07 GMT 2017 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>All Classes</title>
<meta name="date" content="2017-01-26">
<meta name="date" content="2017-02-07">
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
<script type="text/javascript" src="script.js"></script>
</head>
@ -26,10 +26,10 @@
<li><a href="net/corda/flows/AbstractStateReplacementFlow.html" title="class in net.corda.flows" target="classFrame">AbstractStateReplacementFlow</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Acceptor.html" title="class in net.corda.flows" target="classFrame">AbstractStateReplacementFlow.Acceptor</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Instigator.html" title="class in net.corda.flows" target="classFrame">AbstractStateReplacementFlow.Instigator</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Proposal.html" title="interface in net.corda.flows" target="classFrame"><span class="interfaceName">AbstractStateReplacementFlow.Proposal</span></a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Result.html" title="class in net.corda.flows" target="classFrame">AbstractStateReplacementFlow.Result</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Proposal.html" title="class in net.corda.flows" target="classFrame">AbstractStateReplacementFlow.Proposal</a></li>
<li><a href="net/corda/flows/Acceptor.Companion.html" title="class in net.corda.flows" target="classFrame">Acceptor.Companion</a></li>
<li><a href="net/corda/node/services/api/AcceptsFileUpload.html" title="interface in net.corda.node.services.api" target="classFrame"><span class="interfaceName">AcceptsFileUpload</span></a></li>
<li><a href="net/corda/node/services/api/AcceptsFileUpload.DefaultImpls.html" title="class in net.corda.node.services.api" target="classFrame">AcceptsFileUpload.DefaultImpls</a></li>
<li><a href="net/corda/core/contracts/AccrualAdjustment.html" title="enum in net.corda.core.contracts" target="classFrame">AccrualAdjustment</a></li>
<li><a href="net/corda/core/messaging/Ack.html" title="class in net.corda.core.messaging" target="classFrame">Ack</a></li>
<li><a href="net/corda/node/utilities/AddOrRemove.html" title="enum in net.corda.node.utilities" target="classFrame">AddOrRemove</a></li>
@ -49,8 +49,8 @@
<li><a href="net/corda/node/utilities/ANSIProgressRenderer.html" title="class in net.corda.node.utilities" target="classFrame">ANSIProgressRenderer</a></li>
<li><a href="net/corda/core/contracts/clauses/AnyComposition.html" title="class in net.corda.core.contracts.clauses" target="classFrame">AnyComposition</a></li>
<li><a href="net/corda/core/contracts/clauses/AnyOf.html" title="class in net.corda.core.contracts.clauses" target="classFrame">AnyOf</a></li>
<li><a href="net/corda/node/api/APIServer.html" title="interface in net.corda.node.api" target="classFrame"><span class="interfaceName">APIServer</span></a></li>
<li><a href="net/corda/node/internal/APIServerImpl.html" title="class in net.corda.node.internal" target="classFrame">APIServerImpl</a></li>
<li><a href="net/corda/node/webserver/api/APIServer.html" title="interface in net.corda.node.webserver.api" target="classFrame"><span class="interfaceName">APIServer</span></a></li>
<li><a href="net/corda/node/webserver/internal/APIServerImpl.html" title="class in net.corda.node.webserver.internal" target="classFrame">APIServerImpl</a></li>
<li><a href="net/corda/core/utilities/ApiUtils.html" title="class in net.corda.core.utilities" target="classFrame">ApiUtils</a></li>
<li><a href="net/corda/core/flows/AppContext.html" title="class in net.corda.core.flows" target="classFrame">AppContext</a></li>
<li><a href="net/corda/node/ArgsParser.html" title="class in net.corda.node" target="classFrame">ArgsParser</a></li>
@ -64,10 +64,11 @@
<li><a href="net/corda/node/services/messaging/ArtemisMessagingComponent.ServiceAddress.html" title="class in net.corda.node.services.messaging" target="classFrame">ArtemisMessagingComponent.ServiceAddress</a></li>
<li><a href="net/corda/node/services/messaging/ArtemisMessagingServer.html" title="class in net.corda.node.services.messaging" target="classFrame">ArtemisMessagingServer</a></li>
<li><a href="net/corda/node/services/messaging/ArtemisMessagingServer.Companion.html" title="class in net.corda.node.services.messaging" target="classFrame">ArtemisMessagingServer.Companion</a></li>
<li><a href="net/corda/node/ArtemisTestKt.html" title="class in net.corda.node" target="classFrame">ArtemisTestKt</a></li>
<li><a href="net/corda/client/fxutils/AssociatedList.html" title="class in net.corda.client.fxutils" target="classFrame">AssociatedList</a></li>
<li><a href="net/corda/core/contracts/Attachment.html" title="interface in net.corda.core.contracts" target="classFrame"><span class="interfaceName">Attachment</span></a></li>
<li><a href="net/corda/core/contracts/Attachment.DefaultImpls.html" title="class in net.corda.core.contracts" target="classFrame">Attachment.DefaultImpls</a></li>
<li><a href="net/corda/node/servlets/AttachmentDownloadServlet.html" title="class in net.corda.node.servlets" target="classFrame">AttachmentDownloadServlet</a></li>
<li><a href="net/corda/node/webserver/servlets/AttachmentDownloadServlet.html" title="class in net.corda.node.webserver.servlets" target="classFrame">AttachmentDownloadServlet</a></li>
<li><a href="net/corda/core/node/AttachmentsClassLoader.html" title="class in net.corda.core.node" target="classFrame">AttachmentsClassLoader</a></li>
<li><a href="net/corda/core/node/AttachmentsClassLoader.OverlappingAttachments.html" title="class in net.corda.core.node" target="classFrame">AttachmentsClassLoader.OverlappingAttachments</a></li>
<li><a href="net/corda/core/node/services/AttachmentStorage.html" title="interface in net.corda.core.node.services" target="classFrame"><span class="interfaceName">AttachmentStorage</span></a></li>
@ -94,11 +95,9 @@
<li><a href="net/corda/flows/CashCommand.ExitCash.html" title="class in net.corda.flows" target="classFrame">CashCommand.ExitCash</a></li>
<li><a href="net/corda/flows/CashCommand.IssueCash.html" title="class in net.corda.flows" target="classFrame">CashCommand.IssueCash</a></li>
<li><a href="net/corda/flows/CashCommand.PayCash.html" title="class in net.corda.flows" target="classFrame">CashCommand.PayCash</a></li>
<li><a href="net/corda/flows/CashException.html" title="class in net.corda.flows" target="classFrame">CashException</a></li>
<li><a href="net/corda/flows/CashFlow.html" title="class in net.corda.flows" target="classFrame">CashFlow</a></li>
<li><a href="net/corda/flows/CashFlow.Companion.html" title="class in net.corda.flows" target="classFrame">CashFlow.Companion</a></li>
<li><a href="net/corda/flows/CashFlowResult.html" title="class in net.corda.flows" target="classFrame">CashFlowResult</a></li>
<li><a href="net/corda/flows/CashFlowResult.Failed.html" title="class in net.corda.flows" target="classFrame">CashFlowResult.Failed</a></li>
<li><a href="net/corda/flows/CashFlowResult.Success.html" title="class in net.corda.flows" target="classFrame">CashFlowResult.Success</a></li>
<li><a href="net/corda/contracts/asset/CashKt.html" title="class in net.corda.contracts.asset" target="classFrame">CashKt</a></li>
<li><a href="net/corda/schemas/CashSchema.html" title="class in net.corda.schemas" target="classFrame">CashSchema</a></li>
<li><a href="net/corda/schemas/CashSchemaV1.html" title="class in net.corda.schemas" target="classFrame">CashSchemaV1</a></li>
@ -137,6 +136,7 @@
<li><a href="net/corda/contracts/asset/Clauses.Settle.html" title="class in net.corda.contracts.asset" target="classFrame">Clauses.Settle</a></li>
<li><a href="net/corda/contracts/asset/Clauses.VerifyLifecycle.html" title="class in net.corda.contracts.asset" target="classFrame">Clauses.VerifyLifecycle</a></li>
<li><a href="net/corda/core/contracts/clauses/ClauseVerifier.html" title="class in net.corda.core.contracts.clauses" target="classFrame">ClauseVerifier</a></li>
<li><a href="net/corda/node/Client.html" title="class in net.corda.node" target="classFrame">Client</a></li>
<li><a href="net/corda/flows/Client.Companion.html" title="class in net.corda.flows" target="classFrame">Client.Companion</a></li>
<li><a href="net/corda/node/services/messaging/ClientRPCRequestMessage.html" title="class in net.corda.node.services.messaging" target="classFrame">ClientRPCRequestMessage</a></li>
<li><a href="net/corda/node/services/messaging/ClientRPCRequestMessage.Companion.html" title="class in net.corda.node.services.messaging" target="classFrame">ClientRPCRequestMessage.Companion</a></li>
@ -209,7 +209,6 @@
<li><a href="net/corda/flows/Companion.RECEIVING.html" title="class in net.corda.flows" target="classFrame">Companion.RECEIVING</a></li>
<li><a href="net/corda/flows/Companion.RECORDING.html" title="class in net.corda.flows" target="classFrame">Companion.RECORDING</a></li>
<li><a href="net/corda/flows/Companion.RECORDING.html" title="class in net.corda.flows" target="classFrame">Companion.RECORDING</a></li>
<li><a href="net/corda/flows/Companion.REJECTING.html" title="class in net.corda.flows" target="classFrame">Companion.REJECTING</a></li>
<li><a href="net/corda/flows/Companion.REQUESTING.html" title="class in net.corda.flows" target="classFrame">Companion.REQUESTING</a></li>
<li><a href="net/corda/node/services/events/Companion.RUNNING.html" title="class in net.corda.node.services.events" target="classFrame">Companion.RUNNING</a></li>
<li><a href="net/corda/flows/Companion.SENDING_CONFIRM.html" title="class in net.corda.flows" target="classFrame">Companion.SENDING_CONFIRM</a></li>
@ -238,16 +237,16 @@
<li><a href="net/corda/core/serialization/CompositeKeyLeafSerializer.html" title="class in net.corda.core.serialization" target="classFrame">CompositeKeyLeafSerializer</a></li>
<li><a href="net/corda/core/serialization/CompositeKeyNodeSerializer.html" title="class in net.corda.core.serialization" target="classFrame">CompositeKeyNodeSerializer</a></li>
<li><a href="net/corda/client/fxutils/ConcatenatedList.html" title="class in net.corda.client.fxutils" target="classFrame">ConcatenatedList</a></li>
<li><a href="net/corda/node/servlets/Config.html" title="class in net.corda.node.servlets" target="classFrame">Config</a></li>
<li><a href="net/corda/node/services/config/ConfigHelper.html" title="class in net.corda.node.services.config" target="classFrame">ConfigHelper</a></li>
<li><a href="net/corda/node/internal/ConfigurationException.html" title="class in net.corda.node.internal" target="classFrame">ConfigurationException</a></li>
<li><a href="net/corda/node/services/config/ConfigUtilities.html" title="class in net.corda.node.services.config" target="classFrame">ConfigUtilities</a></li>
<li><a href="net/corda/node/utilities/ConfigUtilsKt.html" title="class in net.corda.node.utilities" target="classFrame">ConfigUtilsKt</a></li>
<li><a href="net/corda/node/ConnectionDirection.html" title="class in net.corda.node" target="classFrame">ConnectionDirection</a></li>
<li><a href="net/corda/node/ConnectionDirection.Inbound.html" title="class in net.corda.node" target="classFrame">ConnectionDirection.Inbound</a></li>
<li><a href="net/corda/node/services/messaging/ConnectionDirection.Inbound.html" title="class in net.corda.node.services.messaging" target="classFrame">ConnectionDirection.Inbound</a></li>
<li><a href="net/corda/node/ConnectionDirection.Outbound.html" title="class in net.corda.node" target="classFrame">ConnectionDirection.Outbound</a></li>
<li><a href="net/corda/node/services/messaging/ConnectionDirection.Outbound.html" title="class in net.corda.node.services.messaging" target="classFrame">ConnectionDirection.Outbound</a></li>
<li><a href="net/corda/core/contracts/Contract.html" title="interface in net.corda.core.contracts" target="classFrame"><span class="interfaceName">Contract</span></a></li>
<li><a href="net/corda/node/api/ContractClassRef.html" title="class in net.corda.node.api" target="classFrame">ContractClassRef</a></li>
<li><a href="net/corda/node/api/ContractDefRef.html" title="interface in net.corda.node.api" target="classFrame"><span class="interfaceName">ContractDefRef</span></a></li>
<li><a href="net/corda/node/api/ContractLedgerRef.html" title="class in net.corda.node.api" target="classFrame">ContractLedgerRef</a></li>
<li><a href="net/corda/core/contracts/ContractsDSL.html" title="class in net.corda.core.contracts" target="classFrame">ContractsDSL</a></li>
<li><a href="net/corda/core/contracts/ContractState.html" title="interface in net.corda.core.contracts" target="classFrame"><span class="interfaceName">ContractState</span></a></li>
<li><a href="net/corda/contracts/testing/ContractStateGenerator.html" title="class in net.corda.contracts.testing" target="classFrame">ContractStateGenerator</a></li>
@ -262,15 +261,15 @@
<li><a href="net/corda/node/internal/CordaRPCOpsImpl.html" title="class in net.corda.node.internal" target="classFrame">CordaRPCOpsImpl</a></li>
<li><a href="net/corda/node/internal/CordaRPCOpsImpl.Companion.html" title="class in net.corda.node.internal" target="classFrame">CordaRPCOpsImpl.Companion</a></li>
<li><a href="net/corda/core/messaging/CordaRPCOpsKt.html" title="class in net.corda.core.messaging" target="classFrame">CordaRPCOpsKt</a></li>
<li><a href="net/corda/node/api/Criteria.AllDeals.html" title="class in net.corda.node.api" target="classFrame">Criteria.AllDeals</a></li>
<li><a href="net/corda/node/api/Criteria.Deal.html" title="class in net.corda.node.api" target="classFrame">Criteria.Deal</a></li>
<li><a href="net/corda/node/webserver/api/Criteria.AllDeals.html" title="class in net.corda.node.webserver.api" target="classFrame">Criteria.AllDeals</a></li>
<li><a href="net/corda/node/webserver/api/Criteria.Deal.html" title="class in net.corda.node.webserver.api" target="classFrame">Criteria.Deal</a></li>
<li><a href="net/corda/core/crypto/CryptoUtilities.html" title="class in net.corda.core.crypto" target="classFrame">CryptoUtilities</a></li>
<li><a href="net/corda/core/math/CubicSplineInterpolator.html" title="class in net.corda.core.math" target="classFrame">CubicSplineInterpolator</a></li>
<li><a href="net/corda/core/math/CubicSplineInterpolator.Factory.html" title="class in net.corda.core.math" target="classFrame">CubicSplineInterpolator.Factory</a></li>
<li><a href="net/corda/core/testing/CurrencyGenerator.html" title="class in net.corda.core.testing" target="classFrame">CurrencyGenerator</a></li>
<li><a href="net/corda/core/testing/CurrencyGenerator.Companion.html" title="class in net.corda.core.testing" target="classFrame">CurrencyGenerator.Companion</a></li>
<li><a href="net/corda/node/utilities/DatabaseSupportKt.html" title="class in net.corda.node.utilities" target="classFrame">DatabaseSupportKt</a></li>
<li><a href="net/corda/node/servlets/DataUploadServlet.html" title="class in net.corda.node.servlets" target="classFrame">DataUploadServlet</a></li>
<li><a href="net/corda/node/webserver/servlets/DataUploadServlet.html" title="class in net.corda.node.webserver.servlets" target="classFrame">DataUploadServlet</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.html" title="class in net.corda.node.services.persistence" target="classFrame">DataVending</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.Plugin.html" title="class in net.corda.node.services.persistence" target="classFrame">DataVending.Plugin</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.Service.html" title="class in net.corda.node.services.persistence" target="classFrame">DataVending.Service</a></li>
@ -330,13 +329,14 @@
<li><a href="net/corda/core/contracts/ExpressionSerializer.html" title="class in net.corda.core.contracts" target="classFrame">ExpressionSerializer</a></li>
<li><a href="net/corda/flows/FetchAttachmentsFlow.html" title="class in net.corda.flows" target="classFrame">FetchAttachmentsFlow</a></li>
<li><a href="net/corda/flows/FetchDataFlow.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow</a></li>
<li><a href="net/corda/flows/FetchDataFlow.BadAnswer.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.BadAnswer</a></li>
<li><a href="net/corda/flows/FetchDataFlow.DownloadedVsRequestedDataMismatch.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.DownloadedVsRequestedDataMismatch</a></li>
<li><a href="net/corda/flows/FetchDataFlow.DownloadedVsRequestedSizeMismatch.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.DownloadedVsRequestedSizeMismatch</a></li>
<li><a href="net/corda/flows/FetchDataFlow.HashNotFound.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.HashNotFound</a></li>
<li><a href="net/corda/flows/FetchDataFlow.Request.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.Request</a></li>
<li><a href="net/corda/flows/FetchDataFlow.Result.html" title="class in net.corda.flows" target="classFrame">FetchDataFlow.Result</a></li>
<li><a href="net/corda/flows/FetchTransactionsFlow.html" title="class in net.corda.flows" target="classFrame">FetchTransactionsFlow</a></li>
<li><a href="net/corda/node/utilities/FiberBox.html" title="class in net.corda.node.utilities" target="classFrame">FiberBox</a></li>
<li><a href="net/corda/core/node/services/FileUploader.html" title="interface in net.corda.core.node.services" target="classFrame"><span class="interfaceName">FileUploader</span></a></li>
<li><a href="net/corda/core/transactions/FilteredLeaves.html" title="class in net.corda.core.transactions" target="classFrame">FilteredLeaves</a></li>
<li><a href="net/corda/core/transactions/FilteredTransaction.html" title="class in net.corda.core.transactions" target="classFrame">FilteredTransaction</a></li>
<li><a href="net/corda/core/transactions/FilteredTransaction.Companion.html" title="class in net.corda.core.transactions" target="classFrame">FilteredTransaction.Companion</a></li>
@ -354,16 +354,13 @@
<li><a href="net/corda/core/contracts/FixOf.html" title="class in net.corda.core.contracts" target="classFrame">FixOf</a></li>
<li><a href="net/corda/client/fxutils/FlattenedList.html" title="class in net.corda.client.fxutils" target="classFrame">FlattenedList</a></li>
<li><a href="net/corda/client/fxutils/FlattenedList.WrappedObservableValue.html" title="class in net.corda.client.fxutils" target="classFrame">FlattenedList.WrappedObservableValue</a></li>
<li><a href="net/corda/node/api/FlowClassRef.html" title="class in net.corda.node.api" target="classFrame">FlowClassRef</a></li>
<li><a href="net/corda/core/flows/FlowException.html" title="class in net.corda.core.flows" target="classFrame">FlowException</a></li>
<li><a href="net/corda/core/messaging/FlowHandle.html" title="class in net.corda.core.messaging" target="classFrame">FlowHandle</a></li>
<li><a href="net/corda/node/api/FlowInstanceRef.html" title="class in net.corda.node.api" target="classFrame">FlowInstanceRef</a></li>
<li><a href="net/corda/node/services/statemachine/FlowIORequest.html" title="interface in net.corda.node.services.statemachine" target="classFrame"><span class="interfaceName">FlowIORequest</span></a></li>
<li><a href="net/corda/core/flows/FlowLogic.html" title="class in net.corda.core.flows" target="classFrame">FlowLogic</a></li>
<li><a href="net/corda/core/flows/FlowLogicRef.html" title="class in net.corda.core.flows" target="classFrame">FlowLogicRef</a></li>
<li><a href="net/corda/core/flows/FlowLogicRefFactory.html" title="class in net.corda.core.flows" target="classFrame">FlowLogicRefFactory</a></li>
<li><a href="net/corda/node/api/FlowRef.html" title="interface in net.corda.node.api" target="classFrame"><span class="interfaceName">FlowRef</span></a></li>
<li><a href="net/corda/node/api/FlowRequiringAttention.html" title="class in net.corda.node.api" target="classFrame">FlowRequiringAttention</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionException.html" title="class in net.corda.node.services.statemachine" target="classFrame">FlowSessionException</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionState.Initiated.html" title="class in net.corda.node.services.statemachine" target="classFrame">FlowSessionState.Initiated</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionState.Initiating.html" title="class in net.corda.node.services.statemachine" target="classFrame">FlowSessionState.Initiating</a></li>
<li><a href="net/corda/core/flows/FlowStateMachine.html" title="interface in net.corda.core.flows" target="classFrame"><span class="interfaceName">FlowStateMachine</span></a></li>
@ -406,7 +403,6 @@
<li><a href="net/corda/node/services/transactions/InMemoryUniquenessProvider.html" title="class in net.corda.node.services.transactions" target="classFrame">InMemoryUniquenessProvider</a></li>
<li><a href="net/corda/client/model/InputResolution.Resolved.html" title="class in net.corda.client.model" target="classFrame">InputResolution.Resolved</a></li>
<li><a href="net/corda/client/model/InputResolution.Unresolved.html" title="class in net.corda.client.model" target="classFrame">InputResolution.Unresolved</a></li>
<li><a href="net/corda/flows/InputStateRefResolveFailed.html" title="class in net.corda.flows" target="classFrame">InputStateRefResolveFailed</a></li>
<li><a href="net/corda/core/serialization/InputStreamSerializer.html" title="class in net.corda.core.serialization" target="classFrame">InputStreamSerializer</a></li>
<li><a href="net/corda/node/utilities/InstantColumnType.html" title="class in net.corda.node.utilities" target="classFrame">InstantColumnType</a></li>
<li><a href="net/corda/core/testing/InstantGenerator.html" title="class in net.corda.core.testing" target="classFrame">InstantGenerator</a></li>
@ -435,17 +431,20 @@
<li><a href="net/corda/node/utilities/JsonSupport.CalendarDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.CalendarDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.CompositeKeyDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.CompositeKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.CompositeKeySerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.CompositeKeySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.IdentityObjectMapper.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.IdentityObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.LocalDateDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.LocalDateDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.LocalDateKeyDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.LocalDateKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NodeInfoDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.NodeInfoDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NodeInfoSerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.NodeInfoSerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NoPartyObjectMapper.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.NoPartyObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartyDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.PartyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartyObjectMapper.html" title="interface in net.corda.node.utilities" target="classFrame"><span class="interfaceName">JsonSupport.PartyObjectMapper</span></a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartySerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.PartySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PublicKeyDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.PublicKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PublicKeySerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.PublicKeySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.RpcObjectMapper.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.RpcObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.SecureHashDeserializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.SecureHashDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.SecureHashSerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.SecureHashSerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.ServiceHubObjectMapper.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.ServiceHubObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.ToStringSerializer.html" title="class in net.corda.node.utilities" target="classFrame">JsonSupport.ToStringSerializer</a></li>
<li><a href="net/corda/core/node/services/KeyManagementService.html" title="interface in net.corda.core.node.services" target="classFrame"><span class="interfaceName">KeyManagementService</span></a></li>
<li><a href="net/corda/core/node/services/KeyManagementService.DefaultImpls.html" title="class in net.corda.core.node.services" target="classFrame">KeyManagementService.DefaultImpls</a></li>
@ -485,6 +484,7 @@
<li><a href="net/corda/core/messaging/MessagingService.html" title="interface in net.corda.core.messaging" target="classFrame"><span class="interfaceName">MessagingService</span></a></li>
<li><a href="net/corda/node/services/api/MessagingServiceBuilder.html" title="interface in net.corda.node.services.api" target="classFrame"><span class="interfaceName">MessagingServiceBuilder</span></a></li>
<li><a href="net/corda/node/services/api/MessagingServiceInternal.html" title="interface in net.corda.node.services.api" target="classFrame"><span class="interfaceName">MessagingServiceInternal</span></a></li>
<li><a href="net/corda/node/utilities/MetricsKt.html" title="class in net.corda.node.utilities" target="classFrame">MetricsKt</a></li>
<li><a href="net/corda/core/serialization/MissingAttachmentsException.html" title="class in net.corda.core.serialization" target="classFrame">MissingAttachmentsException</a></li>
<li><a href="net/corda/client/model/Models.html" title="class in net.corda.client.model" target="classFrame">Models</a></li>
<li><a href="net/corda/client/model/ModelsKt.html" title="class in net.corda.client.model" target="classFrame">ModelsKt</a></li>
@ -556,7 +556,6 @@
<li><a href="net/corda/flows/NotaryChangeFlow.html" title="class in net.corda.flows" target="classFrame">NotaryChangeFlow</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Acceptor.html" title="class in net.corda.flows" target="classFrame">NotaryChangeFlow.Acceptor</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Instigator.html" title="class in net.corda.flows" target="classFrame">NotaryChangeFlow.Instigator</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Proposal.html" title="class in net.corda.flows" target="classFrame">NotaryChangeFlow.Proposal</a></li>
<li><a href="net/corda/flows/NotaryError.html" title="class in net.corda.flows" target="classFrame">NotaryError</a></li>
<li><a href="net/corda/flows/NotaryError.Conflict.html" title="class in net.corda.flows" target="classFrame">NotaryError.Conflict</a></li>
<li><a href="net/corda/flows/NotaryError.SignaturesInvalid.html" title="class in net.corda.flows" target="classFrame">NotaryError.SignaturesInvalid</a></li>
@ -566,13 +565,13 @@
<li><a href="net/corda/flows/NotaryException.html" title="class in net.corda.flows" target="classFrame">NotaryException</a></li>
<li><a href="net/corda/flows/NotaryFlow.html" title="class in net.corda.flows" target="classFrame">NotaryFlow</a></li>
<li><a href="net/corda/flows/NotaryFlow.Client.html" title="class in net.corda.flows" target="classFrame">NotaryFlow.Client</a></li>
<li><a href="net/corda/flows/NotaryFlow.Result.html" title="class in net.corda.flows" target="classFrame">NotaryFlow.Result</a></li>
<li><a href="net/corda/flows/NotaryFlow.Service.html" title="class in net.corda.flows" target="classFrame">NotaryFlow.Service</a></li>
<li><a href="net/corda/flows/NotaryFlow.SignRequest.html" title="class in net.corda.flows" target="classFrame">NotaryFlow.SignRequest</a></li>
<li><a href="net/corda/node/services/transactions/NotaryService.html" title="class in net.corda.node.services.transactions" target="classFrame">NotaryService</a></li>
<li><a href="net/corda/contracts/clause/NoZeroSizedOutputs.html" title="class in net.corda.contracts.clause" target="classFrame">NoZeroSizedOutputs</a></li>
<li><a href="net/corda/core/crypto/NullPublicKey.html" title="class in net.corda.core.crypto" target="classFrame">NullPublicKey</a></li>
<li><a href="net/corda/core/crypto/NullSignature.html" title="class in net.corda.core.crypto" target="classFrame">NullSignature</a></li>
<li><a href="net/corda/node/webserver/servlets/ObjectMapperConfig.html" title="class in net.corda.node.webserver.servlets" target="classFrame">ObjectMapperConfig</a></li>
<li><a href="net/corda/contracts/asset/Obligation.html" title="class in net.corda.contracts.asset" target="classFrame">Obligation</a></li>
<li><a href="net/corda/contracts/asset/Obligation.Clauses.html" title="interface in net.corda.contracts.asset" target="classFrame"><span class="interfaceName">Obligation.Clauses</span></a></li>
<li><a href="net/corda/contracts/asset/Obligation.Commands.html" title="interface in net.corda.contracts.asset" target="classFrame"><span class="interfaceName">Obligation.Commands</span></a></li>
@ -650,10 +649,7 @@
<li><a href="net/corda/flows/ResolveTransactionsFlow.html" title="class in net.corda.flows" target="classFrame">ResolveTransactionsFlow</a></li>
<li><a href="net/corda/flows/ResolveTransactionsFlow.Companion.html" title="class in net.corda.flows" target="classFrame">ResolveTransactionsFlow.Companion</a></li>
<li><a href="net/corda/flows/ResolveTransactionsFlow.ExcessivelyLargeTransactionGraph.html" title="class in net.corda.flows" target="classFrame">ResolveTransactionsFlow.ExcessivelyLargeTransactionGraph</a></li>
<li><a href="net/corda/node/servlets/ResponseFilter.html" title="class in net.corda.node.servlets" target="classFrame">ResponseFilter</a></li>
<li><a href="net/corda/flows/Result.Companion.html" title="class in net.corda.flows" target="classFrame">Result.Companion</a></li>
<li><a href="net/corda/flows/Result.Error.html" title="class in net.corda.flows" target="classFrame">Result.Error</a></li>
<li><a href="net/corda/flows/Result.Success.html" title="class in net.corda.flows" target="classFrame">Result.Success</a></li>
<li><a href="net/corda/node/webserver/servlets/ResponseFilter.html" title="class in net.corda.node.webserver.servlets" target="classFrame">ResponseFilter</a></li>
<li><a href="net/corda/core/RetryableException.html" title="class in net.corda.core" target="classFrame">RetryableException</a></li>
<li><a href="net/corda/node/services/messaging/RPCDispatcher.html" title="class in net.corda.node.services.messaging" target="classFrame">RPCDispatcher</a></li>
<li><a href="net/corda/node/services/messaging/RPCException.html" title="class in net.corda.node.services.messaging" target="classFrame">RPCException</a></li>
@ -692,7 +688,7 @@
<li><a href="net/corda/core/serialization/SerializeAsTokenSerializer.Companion.html" title="class in net.corda.core.serialization" target="classFrame">SerializeAsTokenSerializer.Companion</a></li>
<li><a href="net/corda/core/serialization/SerializedBytes.html" title="class in net.corda.core.serialization" target="classFrame">SerializedBytes</a></li>
<li><a href="net/corda/core/serialization/SerializedBytesSerializer.html" title="class in net.corda.core.serialization" target="classFrame">SerializedBytesSerializer</a></li>
<li><a href="net/corda/node/services/persistence/Service.Companion.html" title="class in net.corda.node.services.persistence" target="classFrame">Service.Companion</a></li>
<li><a href="net/corda/node/Server.html" title="class in net.corda.node" target="classFrame">Server</a></li>
<li><a href="net/corda/node/services/persistence/Service.NotifyTransactionHandler.html" title="class in net.corda.node.services.persistence" target="classFrame">Service.NotifyTransactionHandler</a></li>
<li><a href="net/corda/node/utilities/ServiceAffinityExecutor.Companion.html" title="class in net.corda.node.utilities" target="classFrame">ServiceAffinityExecutor.Companion</a></li>
<li><a href="net/corda/core/node/ServiceEntry.html" title="class in net.corda.core.node" target="classFrame">ServiceEntry</a></li>
@ -755,21 +751,23 @@
<li><a href="net/corda/node/utilities/StateRefColumns.html" title="class in net.corda.node.utilities" target="classFrame">StateRefColumns</a></li>
<li><a href="net/corda/core/testing/StateRefGenerator.html" title="class in net.corda.core.testing" target="classFrame">StateRefGenerator</a></li>
<li><a href="net/corda/flows/StateReplacementException.html" title="class in net.corda.flows" target="classFrame">StateReplacementException</a></li>
<li><a href="net/corda/flows/StateReplacementRefused.html" title="class in net.corda.flows" target="classFrame">StateReplacementRefused</a></li>
<li><a href="net/corda/node/api/StatesQuery.html" title="interface in net.corda.node.api" target="classFrame"><span class="interfaceName">StatesQuery</span></a></li>
<li><a href="net/corda/node/api/StatesQuery.Companion.html" title="class in net.corda.node.api" target="classFrame">StatesQuery.Companion</a></li>
<li><a href="net/corda/node/api/StatesQuery.Criteria.html" title="interface in net.corda.node.api" target="classFrame"><span class="interfaceName">StatesQuery.Criteria</span></a></li>
<li><a href="net/corda/node/api/StatesQuery.Selection.html" title="class in net.corda.node.api" target="classFrame">StatesQuery.Selection</a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.html" title="interface in net.corda.node.webserver.api" target="classFrame"><span class="interfaceName">StatesQuery</span></a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Companion.html" title="class in net.corda.node.webserver.api" target="classFrame">StatesQuery.Companion</a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Criteria.html" title="interface in net.corda.node.webserver.api" target="classFrame"><span class="interfaceName">StatesQuery.Criteria</span></a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Selection.html" title="class in net.corda.node.webserver.api" target="classFrame">StatesQuery.Selection</a></li>
<li><a href="net/corda/core/node/services/StorageService.html" title="interface in net.corda.core.node.services" target="classFrame"><span class="interfaceName">StorageService</span></a></li>
<li><a href="net/corda/node/services/persistence/StorageServiceImpl.html" title="class in net.corda.node.services.persistence" target="classFrame">StorageServiceImpl</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.html" title="class in net.corda.node.utilities" target="classFrame">StrandLocalTransactionManager</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.Boundary.html" title="class in net.corda.node.utilities" target="classFrame">StrandLocalTransactionManager.Boundary</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.Companion.html" title="class in net.corda.node.utilities" target="classFrame">StrandLocalTransactionManager.Companion</a></li>
<li><a href="net/corda/core/contracts/StructuresKt.html" title="class in net.corda.core.contracts" target="classFrame">StructuresKt</a></li>
<li><a href="com/cordatest/TContract.html" title="class in com.cordatest" target="classFrame">TContract</a></li>
<li><a href="net/corda/core/contracts/Tenor.html" title="class in net.corda.core.contracts" target="classFrame">Tenor</a></li>
<li><a href="net/corda/core/contracts/Tenor.TimeUnit.html" title="enum in net.corda.core.contracts" target="classFrame">Tenor.TimeUnit</a></li>
<li><a href="net/corda/node/utilities/TestClock.html" title="class in net.corda.node.utilities" target="classFrame">TestClock</a></li>
<li><a href="net/corda/core/utilities/TestConstants.html" title="class in net.corda.core.utilities" target="classFrame">TestConstants</a></li>
<li><a href="com/cordatest/TGenesisCommand.html" title="class in com.cordatest" target="classFrame">TGenesisCommand</a></li>
<li><a href="com/cordatest/TGenesisFlow.html" title="class in com.cordatest" target="classFrame">TGenesisFlow</a></li>
<li><a href="net/corda/core/ThreadBox.html" title="class in net.corda.core" target="classFrame">ThreadBox</a></li>
<li><a href="net/corda/core/contracts/Timestamp.html" title="class in net.corda.core.contracts" target="classFrame">Timestamp</a></li>
<li><a href="net/corda/core/node/services/TimestampChecker.html" title="class in net.corda.core.node.services" target="classFrame">TimestampChecker</a></li>
@ -789,7 +787,6 @@
<li><a href="net/corda/client/model/TrackedDelegate.SubjectDelegate.html" title="class in net.corda.client.model" target="classFrame">TrackedDelegate.SubjectDelegate</a></li>
<li><a href="net/corda/client/model/TrackedDelegate.WritableValueDelegate.html" title="class in net.corda.client.model" target="classFrame">TrackedDelegate.WritableValueDelegate</a></li>
<li><a href="net/corda/core/transactions/TransactionBuilder.html" title="class in net.corda.core.transactions" target="classFrame">TransactionBuilder</a></li>
<li><a href="net/corda/node/api/TransactionBuildStep.html" title="class in net.corda.node.api" target="classFrame">TransactionBuildStep</a></li>
<li><a href="net/corda/core/contracts/TransactionConflictException.html" title="class in net.corda.core.contracts" target="classFrame">TransactionConflictException</a></li>
<li><a href="net/corda/client/model/TransactionCreateStatus.html" title="class in net.corda.client.model" target="classFrame">TransactionCreateStatus</a></li>
<li><a href="net/corda/client/model/TransactionCreateStatus.Failed.html" title="class in net.corda.client.model" target="classFrame">TransactionCreateStatus.Failed</a></li>
@ -816,6 +813,9 @@
<li><a href="net/corda/core/contracts/TransactionVerificationException.SignersMissing.html" title="class in net.corda.core.contracts" target="classFrame">TransactionVerificationException.SignersMissing</a></li>
<li><a href="net/corda/core/contracts/TransactionVerificationException.TransactionMissingEncumbranceException.html" title="class in net.corda.core.contracts" target="classFrame">TransactionVerificationException.TransactionMissingEncumbranceException</a></li>
<li><a href="net/corda/core/TransientProperty.html" title="class in net.corda.core" target="classFrame">TransientProperty</a></li>
<li><a href="com/cordatest/TTxCommand.html" title="class in com.cordatest" target="classFrame">TTxCommand</a></li>
<li><a href="com/cordatest/TTxFlow.html" title="class in com.cordatest" target="classFrame">TTxFlow</a></li>
<li><a href="com/cordatest/TTxState.html" title="class in com.cordatest" target="classFrame">TTxState</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.html" title="class in net.corda.flows" target="classFrame">TwoPartyDealFlow</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.Acceptor.html" title="class in net.corda.flows" target="classFrame">TwoPartyDealFlow.Acceptor</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.AutoOffer.html" title="class in net.corda.flows" target="classFrame">TwoPartyDealFlow.AutoOffer</a></li>
@ -857,6 +857,7 @@
<li><a href="net/corda/core/node/services/VaultService.html" title="interface in net.corda.core.node.services" target="classFrame"><span class="interfaceName">VaultService</span></a></li>
<li><a href="net/corda/core/node/services/VaultService.DefaultImpls.html" title="class in net.corda.core.node.services" target="classFrame">VaultService.DefaultImpls</a></li>
<li><a href="net/corda/node/services/messaging/VerifyingNettyConnectorFactory.html" title="class in net.corda.node.services.messaging" target="classFrame">VerifyingNettyConnectorFactory</a></li>
<li><a href="net/corda/node/webserver/WebServer.html" title="class in net.corda.node.webserver" target="classFrame">WebServer</a></li>
<li><a href="net/corda/contracts/testing/WiredTransactionGenerator.html" title="class in net.corda.contracts.testing" target="classFrame">WiredTransactionGenerator</a></li>
<li><a href="net/corda/node/services/network/WireNodeRegistration.html" title="class in net.corda.node.services.network" target="classFrame">WireNodeRegistration</a></li>
<li><a href="net/corda/core/transactions/WireTransaction.html" title="class in net.corda.core.transactions" target="classFrame">WireTransaction</a></li>

View File

@ -2,10 +2,10 @@
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_102) on Thu Jan 26 16:01:09 GMT 2017 -->
<!-- Generated by javadoc (1.8.0_112) on Tue Feb 07 15:56:07 GMT 2017 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>All Classes</title>
<meta name="date" content="2017-01-26">
<meta name="date" content="2017-02-07">
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
<script type="text/javascript" src="script.js"></script>
</head>
@ -26,10 +26,10 @@
<li><a href="net/corda/flows/AbstractStateReplacementFlow.html" title="class in net.corda.flows">AbstractStateReplacementFlow</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Acceptor.html" title="class in net.corda.flows">AbstractStateReplacementFlow.Acceptor</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Instigator.html" title="class in net.corda.flows">AbstractStateReplacementFlow.Instigator</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Proposal.html" title="interface in net.corda.flows"><span class="interfaceName">AbstractStateReplacementFlow.Proposal</span></a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Result.html" title="class in net.corda.flows">AbstractStateReplacementFlow.Result</a></li>
<li><a href="net/corda/flows/AbstractStateReplacementFlow.Proposal.html" title="class in net.corda.flows">AbstractStateReplacementFlow.Proposal</a></li>
<li><a href="net/corda/flows/Acceptor.Companion.html" title="class in net.corda.flows">Acceptor.Companion</a></li>
<li><a href="net/corda/node/services/api/AcceptsFileUpload.html" title="interface in net.corda.node.services.api"><span class="interfaceName">AcceptsFileUpload</span></a></li>
<li><a href="net/corda/node/services/api/AcceptsFileUpload.DefaultImpls.html" title="class in net.corda.node.services.api">AcceptsFileUpload.DefaultImpls</a></li>
<li><a href="net/corda/core/contracts/AccrualAdjustment.html" title="enum in net.corda.core.contracts">AccrualAdjustment</a></li>
<li><a href="net/corda/core/messaging/Ack.html" title="class in net.corda.core.messaging">Ack</a></li>
<li><a href="net/corda/node/utilities/AddOrRemove.html" title="enum in net.corda.node.utilities">AddOrRemove</a></li>
@ -49,8 +49,8 @@
<li><a href="net/corda/node/utilities/ANSIProgressRenderer.html" title="class in net.corda.node.utilities">ANSIProgressRenderer</a></li>
<li><a href="net/corda/core/contracts/clauses/AnyComposition.html" title="class in net.corda.core.contracts.clauses">AnyComposition</a></li>
<li><a href="net/corda/core/contracts/clauses/AnyOf.html" title="class in net.corda.core.contracts.clauses">AnyOf</a></li>
<li><a href="net/corda/node/api/APIServer.html" title="interface in net.corda.node.api"><span class="interfaceName">APIServer</span></a></li>
<li><a href="net/corda/node/internal/APIServerImpl.html" title="class in net.corda.node.internal">APIServerImpl</a></li>
<li><a href="net/corda/node/webserver/api/APIServer.html" title="interface in net.corda.node.webserver.api"><span class="interfaceName">APIServer</span></a></li>
<li><a href="net/corda/node/webserver/internal/APIServerImpl.html" title="class in net.corda.node.webserver.internal">APIServerImpl</a></li>
<li><a href="net/corda/core/utilities/ApiUtils.html" title="class in net.corda.core.utilities">ApiUtils</a></li>
<li><a href="net/corda/core/flows/AppContext.html" title="class in net.corda.core.flows">AppContext</a></li>
<li><a href="net/corda/node/ArgsParser.html" title="class in net.corda.node">ArgsParser</a></li>
@ -64,10 +64,11 @@
<li><a href="net/corda/node/services/messaging/ArtemisMessagingComponent.ServiceAddress.html" title="class in net.corda.node.services.messaging">ArtemisMessagingComponent.ServiceAddress</a></li>
<li><a href="net/corda/node/services/messaging/ArtemisMessagingServer.html" title="class in net.corda.node.services.messaging">ArtemisMessagingServer</a></li>
<li><a href="net/corda/node/services/messaging/ArtemisMessagingServer.Companion.html" title="class in net.corda.node.services.messaging">ArtemisMessagingServer.Companion</a></li>
<li><a href="net/corda/node/ArtemisTestKt.html" title="class in net.corda.node">ArtemisTestKt</a></li>
<li><a href="net/corda/client/fxutils/AssociatedList.html" title="class in net.corda.client.fxutils">AssociatedList</a></li>
<li><a href="net/corda/core/contracts/Attachment.html" title="interface in net.corda.core.contracts"><span class="interfaceName">Attachment</span></a></li>
<li><a href="net/corda/core/contracts/Attachment.DefaultImpls.html" title="class in net.corda.core.contracts">Attachment.DefaultImpls</a></li>
<li><a href="net/corda/node/servlets/AttachmentDownloadServlet.html" title="class in net.corda.node.servlets">AttachmentDownloadServlet</a></li>
<li><a href="net/corda/node/webserver/servlets/AttachmentDownloadServlet.html" title="class in net.corda.node.webserver.servlets">AttachmentDownloadServlet</a></li>
<li><a href="net/corda/core/node/AttachmentsClassLoader.html" title="class in net.corda.core.node">AttachmentsClassLoader</a></li>
<li><a href="net/corda/core/node/AttachmentsClassLoader.OverlappingAttachments.html" title="class in net.corda.core.node">AttachmentsClassLoader.OverlappingAttachments</a></li>
<li><a href="net/corda/core/node/services/AttachmentStorage.html" title="interface in net.corda.core.node.services"><span class="interfaceName">AttachmentStorage</span></a></li>
@ -94,11 +95,9 @@
<li><a href="net/corda/flows/CashCommand.ExitCash.html" title="class in net.corda.flows">CashCommand.ExitCash</a></li>
<li><a href="net/corda/flows/CashCommand.IssueCash.html" title="class in net.corda.flows">CashCommand.IssueCash</a></li>
<li><a href="net/corda/flows/CashCommand.PayCash.html" title="class in net.corda.flows">CashCommand.PayCash</a></li>
<li><a href="net/corda/flows/CashException.html" title="class in net.corda.flows">CashException</a></li>
<li><a href="net/corda/flows/CashFlow.html" title="class in net.corda.flows">CashFlow</a></li>
<li><a href="net/corda/flows/CashFlow.Companion.html" title="class in net.corda.flows">CashFlow.Companion</a></li>
<li><a href="net/corda/flows/CashFlowResult.html" title="class in net.corda.flows">CashFlowResult</a></li>
<li><a href="net/corda/flows/CashFlowResult.Failed.html" title="class in net.corda.flows">CashFlowResult.Failed</a></li>
<li><a href="net/corda/flows/CashFlowResult.Success.html" title="class in net.corda.flows">CashFlowResult.Success</a></li>
<li><a href="net/corda/contracts/asset/CashKt.html" title="class in net.corda.contracts.asset">CashKt</a></li>
<li><a href="net/corda/schemas/CashSchema.html" title="class in net.corda.schemas">CashSchema</a></li>
<li><a href="net/corda/schemas/CashSchemaV1.html" title="class in net.corda.schemas">CashSchemaV1</a></li>
@ -137,6 +136,7 @@
<li><a href="net/corda/contracts/asset/Clauses.Settle.html" title="class in net.corda.contracts.asset">Clauses.Settle</a></li>
<li><a href="net/corda/contracts/asset/Clauses.VerifyLifecycle.html" title="class in net.corda.contracts.asset">Clauses.VerifyLifecycle</a></li>
<li><a href="net/corda/core/contracts/clauses/ClauseVerifier.html" title="class in net.corda.core.contracts.clauses">ClauseVerifier</a></li>
<li><a href="net/corda/node/Client.html" title="class in net.corda.node">Client</a></li>
<li><a href="net/corda/flows/Client.Companion.html" title="class in net.corda.flows">Client.Companion</a></li>
<li><a href="net/corda/node/services/messaging/ClientRPCRequestMessage.html" title="class in net.corda.node.services.messaging">ClientRPCRequestMessage</a></li>
<li><a href="net/corda/node/services/messaging/ClientRPCRequestMessage.Companion.html" title="class in net.corda.node.services.messaging">ClientRPCRequestMessage.Companion</a></li>
@ -209,7 +209,6 @@
<li><a href="net/corda/flows/Companion.RECEIVING.html" title="class in net.corda.flows">Companion.RECEIVING</a></li>
<li><a href="net/corda/flows/Companion.RECORDING.html" title="class in net.corda.flows">Companion.RECORDING</a></li>
<li><a href="net/corda/flows/Companion.RECORDING.html" title="class in net.corda.flows">Companion.RECORDING</a></li>
<li><a href="net/corda/flows/Companion.REJECTING.html" title="class in net.corda.flows">Companion.REJECTING</a></li>
<li><a href="net/corda/flows/Companion.REQUESTING.html" title="class in net.corda.flows">Companion.REQUESTING</a></li>
<li><a href="net/corda/node/services/events/Companion.RUNNING.html" title="class in net.corda.node.services.events">Companion.RUNNING</a></li>
<li><a href="net/corda/flows/Companion.SENDING_CONFIRM.html" title="class in net.corda.flows">Companion.SENDING_CONFIRM</a></li>
@ -238,16 +237,16 @@
<li><a href="net/corda/core/serialization/CompositeKeyLeafSerializer.html" title="class in net.corda.core.serialization">CompositeKeyLeafSerializer</a></li>
<li><a href="net/corda/core/serialization/CompositeKeyNodeSerializer.html" title="class in net.corda.core.serialization">CompositeKeyNodeSerializer</a></li>
<li><a href="net/corda/client/fxutils/ConcatenatedList.html" title="class in net.corda.client.fxutils">ConcatenatedList</a></li>
<li><a href="net/corda/node/servlets/Config.html" title="class in net.corda.node.servlets">Config</a></li>
<li><a href="net/corda/node/services/config/ConfigHelper.html" title="class in net.corda.node.services.config">ConfigHelper</a></li>
<li><a href="net/corda/node/internal/ConfigurationException.html" title="class in net.corda.node.internal">ConfigurationException</a></li>
<li><a href="net/corda/node/services/config/ConfigUtilities.html" title="class in net.corda.node.services.config">ConfigUtilities</a></li>
<li><a href="net/corda/node/utilities/ConfigUtilsKt.html" title="class in net.corda.node.utilities">ConfigUtilsKt</a></li>
<li><a href="net/corda/node/ConnectionDirection.html" title="class in net.corda.node">ConnectionDirection</a></li>
<li><a href="net/corda/node/ConnectionDirection.Inbound.html" title="class in net.corda.node">ConnectionDirection.Inbound</a></li>
<li><a href="net/corda/node/services/messaging/ConnectionDirection.Inbound.html" title="class in net.corda.node.services.messaging">ConnectionDirection.Inbound</a></li>
<li><a href="net/corda/node/ConnectionDirection.Outbound.html" title="class in net.corda.node">ConnectionDirection.Outbound</a></li>
<li><a href="net/corda/node/services/messaging/ConnectionDirection.Outbound.html" title="class in net.corda.node.services.messaging">ConnectionDirection.Outbound</a></li>
<li><a href="net/corda/core/contracts/Contract.html" title="interface in net.corda.core.contracts"><span class="interfaceName">Contract</span></a></li>
<li><a href="net/corda/node/api/ContractClassRef.html" title="class in net.corda.node.api">ContractClassRef</a></li>
<li><a href="net/corda/node/api/ContractDefRef.html" title="interface in net.corda.node.api"><span class="interfaceName">ContractDefRef</span></a></li>
<li><a href="net/corda/node/api/ContractLedgerRef.html" title="class in net.corda.node.api">ContractLedgerRef</a></li>
<li><a href="net/corda/core/contracts/ContractsDSL.html" title="class in net.corda.core.contracts">ContractsDSL</a></li>
<li><a href="net/corda/core/contracts/ContractState.html" title="interface in net.corda.core.contracts"><span class="interfaceName">ContractState</span></a></li>
<li><a href="net/corda/contracts/testing/ContractStateGenerator.html" title="class in net.corda.contracts.testing">ContractStateGenerator</a></li>
@ -262,15 +261,15 @@
<li><a href="net/corda/node/internal/CordaRPCOpsImpl.html" title="class in net.corda.node.internal">CordaRPCOpsImpl</a></li>
<li><a href="net/corda/node/internal/CordaRPCOpsImpl.Companion.html" title="class in net.corda.node.internal">CordaRPCOpsImpl.Companion</a></li>
<li><a href="net/corda/core/messaging/CordaRPCOpsKt.html" title="class in net.corda.core.messaging">CordaRPCOpsKt</a></li>
<li><a href="net/corda/node/api/Criteria.AllDeals.html" title="class in net.corda.node.api">Criteria.AllDeals</a></li>
<li><a href="net/corda/node/api/Criteria.Deal.html" title="class in net.corda.node.api">Criteria.Deal</a></li>
<li><a href="net/corda/node/webserver/api/Criteria.AllDeals.html" title="class in net.corda.node.webserver.api">Criteria.AllDeals</a></li>
<li><a href="net/corda/node/webserver/api/Criteria.Deal.html" title="class in net.corda.node.webserver.api">Criteria.Deal</a></li>
<li><a href="net/corda/core/crypto/CryptoUtilities.html" title="class in net.corda.core.crypto">CryptoUtilities</a></li>
<li><a href="net/corda/core/math/CubicSplineInterpolator.html" title="class in net.corda.core.math">CubicSplineInterpolator</a></li>
<li><a href="net/corda/core/math/CubicSplineInterpolator.Factory.html" title="class in net.corda.core.math">CubicSplineInterpolator.Factory</a></li>
<li><a href="net/corda/core/testing/CurrencyGenerator.html" title="class in net.corda.core.testing">CurrencyGenerator</a></li>
<li><a href="net/corda/core/testing/CurrencyGenerator.Companion.html" title="class in net.corda.core.testing">CurrencyGenerator.Companion</a></li>
<li><a href="net/corda/node/utilities/DatabaseSupportKt.html" title="class in net.corda.node.utilities">DatabaseSupportKt</a></li>
<li><a href="net/corda/node/servlets/DataUploadServlet.html" title="class in net.corda.node.servlets">DataUploadServlet</a></li>
<li><a href="net/corda/node/webserver/servlets/DataUploadServlet.html" title="class in net.corda.node.webserver.servlets">DataUploadServlet</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.html" title="class in net.corda.node.services.persistence">DataVending</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.Plugin.html" title="class in net.corda.node.services.persistence">DataVending.Plugin</a></li>
<li><a href="net/corda/node/services/persistence/DataVending.Service.html" title="class in net.corda.node.services.persistence">DataVending.Service</a></li>
@ -330,13 +329,14 @@
<li><a href="net/corda/core/contracts/ExpressionSerializer.html" title="class in net.corda.core.contracts">ExpressionSerializer</a></li>
<li><a href="net/corda/flows/FetchAttachmentsFlow.html" title="class in net.corda.flows">FetchAttachmentsFlow</a></li>
<li><a href="net/corda/flows/FetchDataFlow.html" title="class in net.corda.flows">FetchDataFlow</a></li>
<li><a href="net/corda/flows/FetchDataFlow.BadAnswer.html" title="class in net.corda.flows">FetchDataFlow.BadAnswer</a></li>
<li><a href="net/corda/flows/FetchDataFlow.DownloadedVsRequestedDataMismatch.html" title="class in net.corda.flows">FetchDataFlow.DownloadedVsRequestedDataMismatch</a></li>
<li><a href="net/corda/flows/FetchDataFlow.DownloadedVsRequestedSizeMismatch.html" title="class in net.corda.flows">FetchDataFlow.DownloadedVsRequestedSizeMismatch</a></li>
<li><a href="net/corda/flows/FetchDataFlow.HashNotFound.html" title="class in net.corda.flows">FetchDataFlow.HashNotFound</a></li>
<li><a href="net/corda/flows/FetchDataFlow.Request.html" title="class in net.corda.flows">FetchDataFlow.Request</a></li>
<li><a href="net/corda/flows/FetchDataFlow.Result.html" title="class in net.corda.flows">FetchDataFlow.Result</a></li>
<li><a href="net/corda/flows/FetchTransactionsFlow.html" title="class in net.corda.flows">FetchTransactionsFlow</a></li>
<li><a href="net/corda/node/utilities/FiberBox.html" title="class in net.corda.node.utilities">FiberBox</a></li>
<li><a href="net/corda/core/node/services/FileUploader.html" title="interface in net.corda.core.node.services"><span class="interfaceName">FileUploader</span></a></li>
<li><a href="net/corda/core/transactions/FilteredLeaves.html" title="class in net.corda.core.transactions">FilteredLeaves</a></li>
<li><a href="net/corda/core/transactions/FilteredTransaction.html" title="class in net.corda.core.transactions">FilteredTransaction</a></li>
<li><a href="net/corda/core/transactions/FilteredTransaction.Companion.html" title="class in net.corda.core.transactions">FilteredTransaction.Companion</a></li>
@ -354,16 +354,13 @@
<li><a href="net/corda/core/contracts/FixOf.html" title="class in net.corda.core.contracts">FixOf</a></li>
<li><a href="net/corda/client/fxutils/FlattenedList.html" title="class in net.corda.client.fxutils">FlattenedList</a></li>
<li><a href="net/corda/client/fxutils/FlattenedList.WrappedObservableValue.html" title="class in net.corda.client.fxutils">FlattenedList.WrappedObservableValue</a></li>
<li><a href="net/corda/node/api/FlowClassRef.html" title="class in net.corda.node.api">FlowClassRef</a></li>
<li><a href="net/corda/core/flows/FlowException.html" title="class in net.corda.core.flows">FlowException</a></li>
<li><a href="net/corda/core/messaging/FlowHandle.html" title="class in net.corda.core.messaging">FlowHandle</a></li>
<li><a href="net/corda/node/api/FlowInstanceRef.html" title="class in net.corda.node.api">FlowInstanceRef</a></li>
<li><a href="net/corda/node/services/statemachine/FlowIORequest.html" title="interface in net.corda.node.services.statemachine"><span class="interfaceName">FlowIORequest</span></a></li>
<li><a href="net/corda/core/flows/FlowLogic.html" title="class in net.corda.core.flows">FlowLogic</a></li>
<li><a href="net/corda/core/flows/FlowLogicRef.html" title="class in net.corda.core.flows">FlowLogicRef</a></li>
<li><a href="net/corda/core/flows/FlowLogicRefFactory.html" title="class in net.corda.core.flows">FlowLogicRefFactory</a></li>
<li><a href="net/corda/node/api/FlowRef.html" title="interface in net.corda.node.api"><span class="interfaceName">FlowRef</span></a></li>
<li><a href="net/corda/node/api/FlowRequiringAttention.html" title="class in net.corda.node.api">FlowRequiringAttention</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionException.html" title="class in net.corda.node.services.statemachine">FlowSessionException</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionState.Initiated.html" title="class in net.corda.node.services.statemachine">FlowSessionState.Initiated</a></li>
<li><a href="net/corda/node/services/statemachine/FlowSessionState.Initiating.html" title="class in net.corda.node.services.statemachine">FlowSessionState.Initiating</a></li>
<li><a href="net/corda/core/flows/FlowStateMachine.html" title="interface in net.corda.core.flows"><span class="interfaceName">FlowStateMachine</span></a></li>
@ -406,7 +403,6 @@
<li><a href="net/corda/node/services/transactions/InMemoryUniquenessProvider.html" title="class in net.corda.node.services.transactions">InMemoryUniquenessProvider</a></li>
<li><a href="net/corda/client/model/InputResolution.Resolved.html" title="class in net.corda.client.model">InputResolution.Resolved</a></li>
<li><a href="net/corda/client/model/InputResolution.Unresolved.html" title="class in net.corda.client.model">InputResolution.Unresolved</a></li>
<li><a href="net/corda/flows/InputStateRefResolveFailed.html" title="class in net.corda.flows">InputStateRefResolveFailed</a></li>
<li><a href="net/corda/core/serialization/InputStreamSerializer.html" title="class in net.corda.core.serialization">InputStreamSerializer</a></li>
<li><a href="net/corda/node/utilities/InstantColumnType.html" title="class in net.corda.node.utilities">InstantColumnType</a></li>
<li><a href="net/corda/core/testing/InstantGenerator.html" title="class in net.corda.core.testing">InstantGenerator</a></li>
@ -435,17 +431,20 @@
<li><a href="net/corda/node/utilities/JsonSupport.CalendarDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.CalendarDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.CompositeKeyDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.CompositeKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.CompositeKeySerializer.html" title="class in net.corda.node.utilities">JsonSupport.CompositeKeySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.IdentityObjectMapper.html" title="class in net.corda.node.utilities">JsonSupport.IdentityObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.LocalDateDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.LocalDateDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.LocalDateKeyDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.LocalDateKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NodeInfoDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.NodeInfoDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NodeInfoSerializer.html" title="class in net.corda.node.utilities">JsonSupport.NodeInfoSerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.NoPartyObjectMapper.html" title="class in net.corda.node.utilities">JsonSupport.NoPartyObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartyDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.PartyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartyObjectMapper.html" title="interface in net.corda.node.utilities"><span class="interfaceName">JsonSupport.PartyObjectMapper</span></a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PartySerializer.html" title="class in net.corda.node.utilities">JsonSupport.PartySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PublicKeyDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.PublicKeyDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.PublicKeySerializer.html" title="class in net.corda.node.utilities">JsonSupport.PublicKeySerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.RpcObjectMapper.html" title="class in net.corda.node.utilities">JsonSupport.RpcObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.SecureHashDeserializer.html" title="class in net.corda.node.utilities">JsonSupport.SecureHashDeserializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.SecureHashSerializer.html" title="class in net.corda.node.utilities">JsonSupport.SecureHashSerializer</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.ServiceHubObjectMapper.html" title="class in net.corda.node.utilities">JsonSupport.ServiceHubObjectMapper</a></li>
<li><a href="net/corda/node/utilities/JsonSupport.ToStringSerializer.html" title="class in net.corda.node.utilities">JsonSupport.ToStringSerializer</a></li>
<li><a href="net/corda/core/node/services/KeyManagementService.html" title="interface in net.corda.core.node.services"><span class="interfaceName">KeyManagementService</span></a></li>
<li><a href="net/corda/core/node/services/KeyManagementService.DefaultImpls.html" title="class in net.corda.core.node.services">KeyManagementService.DefaultImpls</a></li>
@ -485,6 +484,7 @@
<li><a href="net/corda/core/messaging/MessagingService.html" title="interface in net.corda.core.messaging"><span class="interfaceName">MessagingService</span></a></li>
<li><a href="net/corda/node/services/api/MessagingServiceBuilder.html" title="interface in net.corda.node.services.api"><span class="interfaceName">MessagingServiceBuilder</span></a></li>
<li><a href="net/corda/node/services/api/MessagingServiceInternal.html" title="interface in net.corda.node.services.api"><span class="interfaceName">MessagingServiceInternal</span></a></li>
<li><a href="net/corda/node/utilities/MetricsKt.html" title="class in net.corda.node.utilities">MetricsKt</a></li>
<li><a href="net/corda/core/serialization/MissingAttachmentsException.html" title="class in net.corda.core.serialization">MissingAttachmentsException</a></li>
<li><a href="net/corda/client/model/Models.html" title="class in net.corda.client.model">Models</a></li>
<li><a href="net/corda/client/model/ModelsKt.html" title="class in net.corda.client.model">ModelsKt</a></li>
@ -556,7 +556,6 @@
<li><a href="net/corda/flows/NotaryChangeFlow.html" title="class in net.corda.flows">NotaryChangeFlow</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Acceptor.html" title="class in net.corda.flows">NotaryChangeFlow.Acceptor</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Instigator.html" title="class in net.corda.flows">NotaryChangeFlow.Instigator</a></li>
<li><a href="net/corda/flows/NotaryChangeFlow.Proposal.html" title="class in net.corda.flows">NotaryChangeFlow.Proposal</a></li>
<li><a href="net/corda/flows/NotaryError.html" title="class in net.corda.flows">NotaryError</a></li>
<li><a href="net/corda/flows/NotaryError.Conflict.html" title="class in net.corda.flows">NotaryError.Conflict</a></li>
<li><a href="net/corda/flows/NotaryError.SignaturesInvalid.html" title="class in net.corda.flows">NotaryError.SignaturesInvalid</a></li>
@ -566,13 +565,13 @@
<li><a href="net/corda/flows/NotaryException.html" title="class in net.corda.flows">NotaryException</a></li>
<li><a href="net/corda/flows/NotaryFlow.html" title="class in net.corda.flows">NotaryFlow</a></li>
<li><a href="net/corda/flows/NotaryFlow.Client.html" title="class in net.corda.flows">NotaryFlow.Client</a></li>
<li><a href="net/corda/flows/NotaryFlow.Result.html" title="class in net.corda.flows">NotaryFlow.Result</a></li>
<li><a href="net/corda/flows/NotaryFlow.Service.html" title="class in net.corda.flows">NotaryFlow.Service</a></li>
<li><a href="net/corda/flows/NotaryFlow.SignRequest.html" title="class in net.corda.flows">NotaryFlow.SignRequest</a></li>
<li><a href="net/corda/node/services/transactions/NotaryService.html" title="class in net.corda.node.services.transactions">NotaryService</a></li>
<li><a href="net/corda/contracts/clause/NoZeroSizedOutputs.html" title="class in net.corda.contracts.clause">NoZeroSizedOutputs</a></li>
<li><a href="net/corda/core/crypto/NullPublicKey.html" title="class in net.corda.core.crypto">NullPublicKey</a></li>
<li><a href="net/corda/core/crypto/NullSignature.html" title="class in net.corda.core.crypto">NullSignature</a></li>
<li><a href="net/corda/node/webserver/servlets/ObjectMapperConfig.html" title="class in net.corda.node.webserver.servlets">ObjectMapperConfig</a></li>
<li><a href="net/corda/contracts/asset/Obligation.html" title="class in net.corda.contracts.asset">Obligation</a></li>
<li><a href="net/corda/contracts/asset/Obligation.Clauses.html" title="interface in net.corda.contracts.asset"><span class="interfaceName">Obligation.Clauses</span></a></li>
<li><a href="net/corda/contracts/asset/Obligation.Commands.html" title="interface in net.corda.contracts.asset"><span class="interfaceName">Obligation.Commands</span></a></li>
@ -650,10 +649,7 @@
<li><a href="net/corda/flows/ResolveTransactionsFlow.html" title="class in net.corda.flows">ResolveTransactionsFlow</a></li>
<li><a href="net/corda/flows/ResolveTransactionsFlow.Companion.html" title="class in net.corda.flows">ResolveTransactionsFlow.Companion</a></li>
<li><a href="net/corda/flows/ResolveTransactionsFlow.ExcessivelyLargeTransactionGraph.html" title="class in net.corda.flows">ResolveTransactionsFlow.ExcessivelyLargeTransactionGraph</a></li>
<li><a href="net/corda/node/servlets/ResponseFilter.html" title="class in net.corda.node.servlets">ResponseFilter</a></li>
<li><a href="net/corda/flows/Result.Companion.html" title="class in net.corda.flows">Result.Companion</a></li>
<li><a href="net/corda/flows/Result.Error.html" title="class in net.corda.flows">Result.Error</a></li>
<li><a href="net/corda/flows/Result.Success.html" title="class in net.corda.flows">Result.Success</a></li>
<li><a href="net/corda/node/webserver/servlets/ResponseFilter.html" title="class in net.corda.node.webserver.servlets">ResponseFilter</a></li>
<li><a href="net/corda/core/RetryableException.html" title="class in net.corda.core">RetryableException</a></li>
<li><a href="net/corda/node/services/messaging/RPCDispatcher.html" title="class in net.corda.node.services.messaging">RPCDispatcher</a></li>
<li><a href="net/corda/node/services/messaging/RPCException.html" title="class in net.corda.node.services.messaging">RPCException</a></li>
@ -692,7 +688,7 @@
<li><a href="net/corda/core/serialization/SerializeAsTokenSerializer.Companion.html" title="class in net.corda.core.serialization">SerializeAsTokenSerializer.Companion</a></li>
<li><a href="net/corda/core/serialization/SerializedBytes.html" title="class in net.corda.core.serialization">SerializedBytes</a></li>
<li><a href="net/corda/core/serialization/SerializedBytesSerializer.html" title="class in net.corda.core.serialization">SerializedBytesSerializer</a></li>
<li><a href="net/corda/node/services/persistence/Service.Companion.html" title="class in net.corda.node.services.persistence">Service.Companion</a></li>
<li><a href="net/corda/node/Server.html" title="class in net.corda.node">Server</a></li>
<li><a href="net/corda/node/services/persistence/Service.NotifyTransactionHandler.html" title="class in net.corda.node.services.persistence">Service.NotifyTransactionHandler</a></li>
<li><a href="net/corda/node/utilities/ServiceAffinityExecutor.Companion.html" title="class in net.corda.node.utilities">ServiceAffinityExecutor.Companion</a></li>
<li><a href="net/corda/core/node/ServiceEntry.html" title="class in net.corda.core.node">ServiceEntry</a></li>
@ -755,21 +751,23 @@
<li><a href="net/corda/node/utilities/StateRefColumns.html" title="class in net.corda.node.utilities">StateRefColumns</a></li>
<li><a href="net/corda/core/testing/StateRefGenerator.html" title="class in net.corda.core.testing">StateRefGenerator</a></li>
<li><a href="net/corda/flows/StateReplacementException.html" title="class in net.corda.flows">StateReplacementException</a></li>
<li><a href="net/corda/flows/StateReplacementRefused.html" title="class in net.corda.flows">StateReplacementRefused</a></li>
<li><a href="net/corda/node/api/StatesQuery.html" title="interface in net.corda.node.api"><span class="interfaceName">StatesQuery</span></a></li>
<li><a href="net/corda/node/api/StatesQuery.Companion.html" title="class in net.corda.node.api">StatesQuery.Companion</a></li>
<li><a href="net/corda/node/api/StatesQuery.Criteria.html" title="interface in net.corda.node.api"><span class="interfaceName">StatesQuery.Criteria</span></a></li>
<li><a href="net/corda/node/api/StatesQuery.Selection.html" title="class in net.corda.node.api">StatesQuery.Selection</a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.html" title="interface in net.corda.node.webserver.api"><span class="interfaceName">StatesQuery</span></a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Companion.html" title="class in net.corda.node.webserver.api">StatesQuery.Companion</a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Criteria.html" title="interface in net.corda.node.webserver.api"><span class="interfaceName">StatesQuery.Criteria</span></a></li>
<li><a href="net/corda/node/webserver/api/StatesQuery.Selection.html" title="class in net.corda.node.webserver.api">StatesQuery.Selection</a></li>
<li><a href="net/corda/core/node/services/StorageService.html" title="interface in net.corda.core.node.services"><span class="interfaceName">StorageService</span></a></li>
<li><a href="net/corda/node/services/persistence/StorageServiceImpl.html" title="class in net.corda.node.services.persistence">StorageServiceImpl</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.html" title="class in net.corda.node.utilities">StrandLocalTransactionManager</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.Boundary.html" title="class in net.corda.node.utilities">StrandLocalTransactionManager.Boundary</a></li>
<li><a href="net/corda/node/utilities/StrandLocalTransactionManager.Companion.html" title="class in net.corda.node.utilities">StrandLocalTransactionManager.Companion</a></li>
<li><a href="net/corda/core/contracts/StructuresKt.html" title="class in net.corda.core.contracts">StructuresKt</a></li>
<li><a href="com/cordatest/TContract.html" title="class in com.cordatest">TContract</a></li>
<li><a href="net/corda/core/contracts/Tenor.html" title="class in net.corda.core.contracts">Tenor</a></li>
<li><a href="net/corda/core/contracts/Tenor.TimeUnit.html" title="enum in net.corda.core.contracts">Tenor.TimeUnit</a></li>
<li><a href="net/corda/node/utilities/TestClock.html" title="class in net.corda.node.utilities">TestClock</a></li>
<li><a href="net/corda/core/utilities/TestConstants.html" title="class in net.corda.core.utilities">TestConstants</a></li>
<li><a href="com/cordatest/TGenesisCommand.html" title="class in com.cordatest">TGenesisCommand</a></li>
<li><a href="com/cordatest/TGenesisFlow.html" title="class in com.cordatest">TGenesisFlow</a></li>
<li><a href="net/corda/core/ThreadBox.html" title="class in net.corda.core">ThreadBox</a></li>
<li><a href="net/corda/core/contracts/Timestamp.html" title="class in net.corda.core.contracts">Timestamp</a></li>
<li><a href="net/corda/core/node/services/TimestampChecker.html" title="class in net.corda.core.node.services">TimestampChecker</a></li>
@ -789,7 +787,6 @@
<li><a href="net/corda/client/model/TrackedDelegate.SubjectDelegate.html" title="class in net.corda.client.model">TrackedDelegate.SubjectDelegate</a></li>
<li><a href="net/corda/client/model/TrackedDelegate.WritableValueDelegate.html" title="class in net.corda.client.model">TrackedDelegate.WritableValueDelegate</a></li>
<li><a href="net/corda/core/transactions/TransactionBuilder.html" title="class in net.corda.core.transactions">TransactionBuilder</a></li>
<li><a href="net/corda/node/api/TransactionBuildStep.html" title="class in net.corda.node.api">TransactionBuildStep</a></li>
<li><a href="net/corda/core/contracts/TransactionConflictException.html" title="class in net.corda.core.contracts">TransactionConflictException</a></li>
<li><a href="net/corda/client/model/TransactionCreateStatus.html" title="class in net.corda.client.model">TransactionCreateStatus</a></li>
<li><a href="net/corda/client/model/TransactionCreateStatus.Failed.html" title="class in net.corda.client.model">TransactionCreateStatus.Failed</a></li>
@ -816,6 +813,9 @@
<li><a href="net/corda/core/contracts/TransactionVerificationException.SignersMissing.html" title="class in net.corda.core.contracts">TransactionVerificationException.SignersMissing</a></li>
<li><a href="net/corda/core/contracts/TransactionVerificationException.TransactionMissingEncumbranceException.html" title="class in net.corda.core.contracts">TransactionVerificationException.TransactionMissingEncumbranceException</a></li>
<li><a href="net/corda/core/TransientProperty.html" title="class in net.corda.core">TransientProperty</a></li>
<li><a href="com/cordatest/TTxCommand.html" title="class in com.cordatest">TTxCommand</a></li>
<li><a href="com/cordatest/TTxFlow.html" title="class in com.cordatest">TTxFlow</a></li>
<li><a href="com/cordatest/TTxState.html" title="class in com.cordatest">TTxState</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.html" title="class in net.corda.flows">TwoPartyDealFlow</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.Acceptor.html" title="class in net.corda.flows">TwoPartyDealFlow.Acceptor</a></li>
<li><a href="net/corda/flows/TwoPartyDealFlow.AutoOffer.html" title="class in net.corda.flows">TwoPartyDealFlow.AutoOffer</a></li>
@ -857,6 +857,7 @@
<li><a href="net/corda/core/node/services/VaultService.html" title="interface in net.corda.core.node.services"><span class="interfaceName">VaultService</span></a></li>
<li><a href="net/corda/core/node/services/VaultService.DefaultImpls.html" title="class in net.corda.core.node.services">VaultService.DefaultImpls</a></li>
<li><a href="net/corda/node/services/messaging/VerifyingNettyConnectorFactory.html" title="class in net.corda.node.services.messaging">VerifyingNettyConnectorFactory</a></li>
<li><a href="net/corda/node/webserver/WebServer.html" title="class in net.corda.node.webserver">WebServer</a></li>
<li><a href="net/corda/contracts/testing/WiredTransactionGenerator.html" title="class in net.corda.contracts.testing">WiredTransactionGenerator</a></li>
<li><a href="net/corda/node/services/network/WireNodeRegistration.html" title="class in net.corda.node.services.network">WireNodeRegistration</a></li>
<li><a href="net/corda/core/transactions/WireTransaction.html" title="class in net.corda.core.transactions">WireTransaction</a></li>

View File

@ -0,0 +1,299 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_112) on Tue Feb 07 15:55:50 GMT 2017 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TContract</title>
<meta name="date" content="2017-02-07">
<meta name="keywords" content="com.cordatest.TContract class">
<meta name="keywords" content="getLegalContractReference()">
<meta name="keywords" content="verify()">
<link rel="stylesheet" type="text/css" href="../../stylesheet.css" title="Style">
<script type="text/javascript" src="../../script.js"></script>
</head>
<body>
<script type="text/javascript"><!--
try {
if (location.href.indexOf('is-external=true') == -1) {
parent.document.title="TContract";
}
}
catch(err) {
}
//-->
var methods = {"i0":10,"i1":10};
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]};
var altColor = "altColor";
var rowColor = "rowColor";
var tableTab = "tableTab";
var activeTableTab = "activeTableTab";
</script>
<noscript>
<div>JavaScript is disabled on your browser.</div>
</noscript>
<!-- ========= START OF TOP NAVBAR ======= -->
<div class="topNav"><a name="navbar.top">
<!-- -->
</a>
<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div>
<a name="navbar.top.firstrow">
<!-- -->
</a>
<ul class="navList" title="Navigation">
<li><a href="../../overview-summary.html">Overview</a></li>
<li><a href="package-summary.html">Package</a></li>
<li class="navBarCell1Rev">Class</li>
<li><a href="package-tree.html">Tree</a></li>
<li><a href="../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../index-all.html">Index</a></li>
<li><a href="../../help-doc.html">Help</a></li>
</ul>
</div>
<div class="subNav">
<ul class="navList">
<li>Prev&nbsp;Class</li>
<li><a href="../../com/cordatest/TGenesisCommand.html" title="class in com.cordatest"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
</ul>
<ul class="navList">
<li><a href="../../index.html?com/cordatest/TContract.html" target="_top">Frames</a></li>
<li><a href="TContract.html" target="_top">No&nbsp;Frames</a></li>
</ul>
<ul class="navList" id="allclasses_navbar_top">
<li><a href="../../allclasses-noframe.html">All&nbsp;Classes</a></li>
</ul>
<div>
<script type="text/javascript"><!--
allClassesLink = document.getElementById("allclasses_navbar_top");
if(window==top) {
allClassesLink.style.display = "block";
}
else {
allClassesLink.style.display = "none";
}
//-->
</script>
</div>
<div>
<ul class="subNavList">
<li>Summary:&nbsp;</li>
<li>Nested&nbsp;|&nbsp;</li>
<li>Field&nbsp;|&nbsp;</li>
<li><a href="#constructor.summary">Constr</a>&nbsp;|&nbsp;</li>
<li><a href="#method.summary">Method</a></li>
</ul>
<ul class="subNavList">
<li>Detail:&nbsp;</li>
<li>Field&nbsp;|&nbsp;</li>
<li><a href="#constructor.detail">Constr</a>&nbsp;|&nbsp;</li>
<li><a href="#method.detail">Method</a></li>
</ul>
</div>
<a name="skip.navbar.top">
<!-- -->
</a></div>
<!-- ========= END OF TOP NAVBAR ========= -->
<!-- ======== START OF CLASS DATA ======== -->
<div class="header">
<div class="subTitle">com.cordatest</div>
<h2 title="Class TContract" class="title">Class TContract</h2>
</div>
<div class="contentContainer">
<ul class="inheritance">
<li>com.cordatest.TContract</li>
</ul>
<div class="description">
<ul class="blockList">
<li class="blockList">
<dl>
<dt>All Implemented Interfaces:</dt>
<dd><a href="../../net/corda/core/contracts/Contract.html" title="interface in net.corda.core.contracts">Contract</a></dd>
</dl>
<hr>
<br>
<pre>public class <span class="typeNameLabel">TContract</span>
implements <a href="../../net/corda/core/contracts/Contract.html" title="interface in net.corda.core.contracts">Contract</a></pre>
</li>
</ul>
</div>
<div class="summary">
<ul class="blockList">
<li class="blockList">
<!-- ======== CONSTRUCTOR SUMMARY ======== -->
<ul class="blockList">
<li class="blockList"><a name="constructor.summary">
<!-- -->
</a>
<h3>Constructor Summary</h3>
<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Constructor Summary table, listing constructors, and an explanation">
<caption><span>Constructors</span><span class="tabEnd">&nbsp;</span></caption>
<tr>
<th class="colOne" scope="col">Constructor and Description</th>
</tr>
<tr class="altColor">
<td class="colOne"><code><span class="memberNameLink"><a href="../../com/cordatest/TContract.html#TContract--">TContract</a></span>()</code>&nbsp;</td>
</tr>
</table>
</li>
</ul>
<!-- ========== METHOD SUMMARY =========== -->
<ul class="blockList">
<li class="blockList"><a name="method.summary">
<!-- -->
</a>
<h3>Method Summary</h3>
<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Method Summary table, listing methods, and an explanation">
<caption><span id="t0" class="activeTableTab"><span>All Methods</span><span class="tabEnd">&nbsp;</span></span><span id="t2" class="tableTab"><span><a href="javascript:show(2);">Instance Methods</a></span><span class="tabEnd">&nbsp;</span></span><span id="t4" class="tableTab"><span><a href="javascript:show(8);">Concrete Methods</a></span><span class="tabEnd">&nbsp;</span></span></caption>
<tr>
<th class="colFirst" scope="col">Modifier and Type</th>
<th class="colLast" scope="col">Method and Description</th>
</tr>
<tr id="i0" class="altColor">
<td class="colFirst"><code><a href="../../net/corda/core/crypto/SecureHash.html" title="type parameter in SecureHash">SecureHash</a></code></td>
<td class="colLast"><code><span class="memberNameLink"><a href="../../com/cordatest/TContract.html#getLegalContractReference--">getLegalContractReference</a></span>()</code>
<div class="block">Unparsed reference to the natural language contract that this code is supposed to express (usually a hash of
the contract's contents).</div>
</td>
</tr>
<tr id="i1" class="rowColor">
<td class="colFirst"><code>void</code></td>
<td class="colLast"><code><span class="memberNameLink"><a href="../../com/cordatest/TContract.html#verify-tx-">verify</a></span>(<a href="../../net/corda/core/contracts/TransactionForContract.html" title="type parameter in TransactionForContract">TransactionForContract</a>&nbsp;tx)</code>
<div class="block">Takes an object that represents a state transition, and ensures the inputs/outputs/commands make sense.
Must throw an exception if there's a problem that should prevent state transition. Takes a single object
rather than an argument so that additional data can be added without breaking binary compatibility with
existing contract code.</div>
</td>
</tr>
</table>
<ul class="blockList">
<li class="blockList"><a name="methods.inherited.from.class.net.corda.core.contracts.Contract">
<!-- -->
</a>
<h3>Methods inherited from interface&nbsp;net.corda.core.contracts.<a href="../../net/corda/core/contracts/Contract.html" title="interface in net.corda.core.contracts">Contract</a></h3>
<code><a href="../../net/corda/core/contracts/Contract.html#getLegalContractReference--">getLegalContractReference</a>, <a href="../../net/corda/core/contracts/Contract.html#verify-tx-">verify</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div class="details">
<ul class="blockList">
<li class="blockList">
<!-- ========= CONSTRUCTOR DETAIL ======== -->
<ul class="blockList">
<li class="blockList"><a name="constructor.detail">
<!-- -->
</a>
<h3>Constructor Detail</h3>
<a name="TContract--">
<!-- -->
</a>
<ul class="blockListLast">
<li class="blockList">
<h4>TContract</h4>
<pre>public&nbsp;TContract()</pre>
</li>
</ul>
</li>
</ul>
<!-- ============ METHOD DETAIL ========== -->
<ul class="blockList">
<li class="blockList"><a name="method.detail">
<!-- -->
</a>
<h3>Method Detail</h3>
<a name="getLegalContractReference--">
<!-- -->
</a>
<ul class="blockList">
<li class="blockList">
<h4>getLegalContractReference</h4>
<pre>public&nbsp;<a href="../../net/corda/core/crypto/SecureHash.html" title="type parameter in SecureHash">SecureHash</a>&nbsp;getLegalContractReference()</pre>
<div class="block"><p><p>Unparsed reference to the natural language contract that this code is supposed to express (usually a hash of
the contract's contents).</p></p></div>
</li>
</ul>
<a name="verify-tx-">
<!-- -->
</a>
<ul class="blockListLast">
<li class="blockList">
<h4>verify</h4>
<pre>public&nbsp;void&nbsp;verify(<a href="../../net/corda/core/contracts/TransactionForContract.html" title="type parameter in TransactionForContract">TransactionForContract</a>&nbsp;tx)</pre>
<div class="block"><p><p>Takes an object that represents a state transition, and ensures the inputs/outputs/commands make sense.
Must throw an exception if there's a problem that should prevent state transition. Takes a single object
rather than an argument so that additional data can be added without breaking binary compatibility with
existing contract code.</p></p></div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
<!-- ========= END OF CLASS DATA ========= -->
<!-- ======= START OF BOTTOM NAVBAR ====== -->
<div class="bottomNav"><a name="navbar.bottom">
<!-- -->
</a>
<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div>
<a name="navbar.bottom.firstrow">
<!-- -->
</a>
<ul class="navList" title="Navigation">
<li><a href="../../overview-summary.html">Overview</a></li>
<li><a href="package-summary.html">Package</a></li>
<li class="navBarCell1Rev">Class</li>
<li><a href="package-tree.html">Tree</a></li>
<li><a href="../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../index-all.html">Index</a></li>
<li><a href="../../help-doc.html">Help</a></li>
</ul>
</div>
<div class="subNav">
<ul class="navList">
<li>Prev&nbsp;Class</li>
<li><a href="../../com/cordatest/TGenesisCommand.html" title="class in com.cordatest"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
</ul>
<ul class="navList">
<li><a href="../../index.html?com/cordatest/TContract.html" target="_top">Frames</a></li>
<li><a href="TContract.html" target="_top">No&nbsp;Frames</a></li>
</ul>
<ul class="navList" id="allclasses_navbar_bottom">
<li><a href="../../allclasses-noframe.html">All&nbsp;Classes</a></li>
</ul>
<div>
<script type="text/javascript"><!--
allClassesLink = document.getElementById("allclasses_navbar_bottom");
if(window==top) {
allClassesLink.style.display = "block";
}
else {
allClassesLink.style.display = "none";
}
//-->
</script>
</div>
<div>
<ul class="subNavList">
<li>Summary:&nbsp;</li>
<li>Nested&nbsp;|&nbsp;</li>
<li>Field&nbsp;|&nbsp;</li>
<li><a href="#constructor.summary">Constr</a>&nbsp;|&nbsp;</li>
<li><a href="#method.summary">Method</a></li>
</ul>
<ul class="subNavList">
<li>Detail:&nbsp;</li>
<li>Field&nbsp;|&nbsp;</li>
<li><a href="#constructor.detail">Constr</a>&nbsp;|&nbsp;</li>
<li><a href="#method.detail">Method</a></li>
</ul>
</div>
<a name="skip.navbar.bottom">
<!-- -->
</a></div>
<!-- ======== END OF BOTTOM NAVBAR ======= -->
</body>
</html>

View File

@ -2,19 +2,19 @@
<!-- NewPage -->
<html lang="en">
<head>
<!-- Generated by javadoc (1.8.0_102) on Thu Jan 26 16:00:44 GMT 2017 -->
<!-- Generated by javadoc (1.8.0_112) on Tue Feb 07 15:55:50 GMT 2017 -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>FetchDataFlow.BadAnswer</title>
<meta name="date" content="2017-01-26">
<meta name="keywords" content="net.corda.flows.FetchDataFlow.BadAnswer class">
<link rel="stylesheet" type="text/css" href="../../../stylesheet.css" title="Style">
<script type="text/javascript" src="../../../script.js"></script>
<title>TGenesisCommand</title>
<meta name="date" content="2017-02-07">
<meta name="keywords" content="com.cordatest.TGenesisCommand class">
<link rel="stylesheet" type="text/css" href="../../stylesheet.css" title="Style">
<script type="text/javascript" src="../../script.js"></script>
</head>
<body>
<script type="text/javascript"><!--
try {
if (location.href.indexOf('is-external=true') == -1) {
parent.document.title="FetchDataFlow.BadAnswer";
parent.document.title="TGenesisCommand";
}
}
catch(err) {
@ -33,26 +33,26 @@
<!-- -->
</a>
<ul class="navList" title="Navigation">
<li><a href="../../../overview-summary.html">Overview</a></li>
<li><a href="../../overview-summary.html">Overview</a></li>
<li><a href="package-summary.html">Package</a></li>
<li class="navBarCell1Rev">Class</li>
<li><a href="package-tree.html">Tree</a></li>
<li><a href="../../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../../index-all.html">Index</a></li>
<li><a href="../../../help-doc.html">Help</a></li>
<li><a href="../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../index-all.html">Index</a></li>
<li><a href="../../help-doc.html">Help</a></li>
</ul>
</div>
<div class="subNav">
<ul class="navList">
<li><a href="../../../net/corda/flows/Companion.BROADCASTING.html" title="class in net.corda.flows"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
<li><a href="../../../net/corda/flows/BroadcastTransactionFlow.html" title="class in net.corda.flows"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
<li><a href="../../com/cordatest/TContract.html" title="class in com.cordatest"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
<li><a href="../../com/cordatest/TGenesisFlow.html" title="class in com.cordatest"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
</ul>
<ul class="navList">
<li><a href="../../../index.html?net/corda/flows/FetchDataFlow.BadAnswer.html" target="_top">Frames</a></li>
<li><a href="FetchDataFlow.BadAnswer.html" target="_top">No&nbsp;Frames</a></li>
<li><a href="../../index.html?com/cordatest/TGenesisCommand.html" target="_top">Frames</a></li>
<li><a href="TGenesisCommand.html" target="_top">No&nbsp;Frames</a></li>
</ul>
<ul class="navList" id="allclasses_navbar_top">
<li><a href="../../../allclasses-noframe.html">All&nbsp;Classes</a></li>
<li><a href="../../allclasses-noframe.html">All&nbsp;Classes</a></li>
</ul>
<div>
<script type="text/javascript"><!--
@ -87,20 +87,24 @@
<!-- ========= END OF TOP NAVBAR ========= -->
<!-- ======== START OF CLASS DATA ======== -->
<div class="header">
<div class="subTitle">net.corda.flows</div>
<h2 title="Class FetchDataFlow.BadAnswer" class="title">Class FetchDataFlow.BadAnswer</h2>
<div class="subTitle">com.cordatest</div>
<h2 title="Class TGenesisCommand" class="title">Class TGenesisCommand</h2>
</div>
<div class="contentContainer">
<ul class="inheritance">
<li>com.cordatest.TGenesisCommand</li>
</ul>
<div class="description">
<ul class="blockList">
<li class="blockList">
<dl>
<dt>Enclosing class:</dt>
<dd><a href="../../../net/corda/flows/FetchDataFlow.html" title="class in net.corda.flows">FetchDataFlow</a>&lt;<a href="../../../net/corda/flows/FetchDataFlow.html" title="type parameter in FetchDataFlow">T</a> extends <a href="../../../net/corda/core/contracts/NamedByHash.html" title="type parameter in NamedByHash">NamedByHash</a>,<a href="../../../net/corda/flows/FetchDataFlow.html" title="type parameter in FetchDataFlow">W</a>&gt;</dd>
<dt>All Implemented Interfaces:</dt>
<dd><a href="../../net/corda/core/contracts/CommandData.html" title="interface in net.corda.core.contracts">CommandData</a></dd>
</dl>
<hr>
<br>
<pre>public static class <span class="typeNameLabel">FetchDataFlow.BadAnswer</span></pre>
<pre>public class <span class="typeNameLabel">TGenesisCommand</span>
implements <a href="../../net/corda/core/contracts/CommandData.html" title="interface in net.corda.core.contracts">CommandData</a></pre>
</li>
</ul>
</div>
@ -119,7 +123,7 @@
<th class="colOne" scope="col">Constructor and Description</th>
</tr>
<tr class="altColor">
<td class="colOne"><code><span class="memberNameLink"><a href="../../../net/corda/flows/FetchDataFlow.BadAnswer.html#BadAnswer--">BadAnswer</a></span>()</code>&nbsp;</td>
<td class="colOne"><code><span class="memberNameLink"><a href="../../com/cordatest/TGenesisCommand.html#TGenesisCommand--">TGenesisCommand</a></span>()</code>&nbsp;</td>
</tr>
</table>
</li>
@ -136,13 +140,13 @@
<!-- -->
</a>
<h3>Constructor Detail</h3>
<a name="BadAnswer--">
<a name="TGenesisCommand--">
<!-- -->
</a>
<ul class="blockListLast">
<li class="blockList">
<h4>BadAnswer</h4>
<pre>public&nbsp;BadAnswer()</pre>
<h4>TGenesisCommand</h4>
<pre>public&nbsp;TGenesisCommand()</pre>
</li>
</ul>
</li>
@ -161,26 +165,26 @@
<!-- -->
</a>
<ul class="navList" title="Navigation">
<li><a href="../../../overview-summary.html">Overview</a></li>
<li><a href="../../overview-summary.html">Overview</a></li>
<li><a href="package-summary.html">Package</a></li>
<li class="navBarCell1Rev">Class</li>
<li><a href="package-tree.html">Tree</a></li>
<li><a href="../../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../../index-all.html">Index</a></li>
<li><a href="../../../help-doc.html">Help</a></li>
<li><a href="../../deprecated-list.html">Deprecated</a></li>
<li><a href="../../index-all.html">Index</a></li>
<li><a href="../../help-doc.html">Help</a></li>
</ul>
</div>
<div class="subNav">
<ul class="navList">
<li><a href="../../../net/corda/flows/Companion.BROADCASTING.html" title="class in net.corda.flows"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
<li><a href="../../../net/corda/flows/BroadcastTransactionFlow.html" title="class in net.corda.flows"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
<li><a href="../../com/cordatest/TContract.html" title="class in com.cordatest"><span class="typeNameLink">Prev&nbsp;Class</span></a></li>
<li><a href="../../com/cordatest/TGenesisFlow.html" title="class in com.cordatest"><span class="typeNameLink">Next&nbsp;Class</span></a></li>
</ul>
<ul class="navList">
<li><a href="../../../index.html?net/corda/flows/FetchDataFlow.BadAnswer.html" target="_top">Frames</a></li>
<li><a href="FetchDataFlow.BadAnswer.html" target="_top">No&nbsp;Frames</a></li>
<li><a href="../../index.html?com/cordatest/TGenesisCommand.html" target="_top">Frames</a></li>
<li><a href="TGenesisCommand.html" target="_top">No&nbsp;Frames</a></li>
</ul>
<ul class="navList" id="allclasses_navbar_bottom">
<li><a href="../../../allclasses-noframe.html">All&nbsp;Classes</a></li>
<li><a href="../../allclasses-noframe.html">All&nbsp;Classes</a></li>
</ul>
<div>
<script type="text/javascript"><!--

Some files were not shown because too many files have changed in this diff Show More