Merge pull request #1082 from corda/mnesbit-remove-transactionforcontract

Remove TransactionForContract and just use LedgerTransaction
This commit is contained in:
Matthew Nesbit 2017-07-20 10:02:04 +01:00 committed by GitHub
commit 2a70be66e5
54 changed files with 339 additions and 333 deletions

View File

@ -6,7 +6,11 @@ import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.* import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.MissingAttachmentsException
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.serialize
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
@ -205,7 +209,7 @@ interface LinearState : ContractState {
*/ */
@CordaSerializable @CordaSerializable
class ClauseVerifier<in S : LinearState, C : CommandData> : Clause<S, C, Unit>() { class ClauseVerifier<in S : LinearState, C : CommandData> : Clause<S, C, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,
@ -399,7 +403,7 @@ interface Contract {
* existing contract code. * existing contract code.
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun verify(tx: TransactionForContract) fun verify(tx: LedgerTransaction)
/** /**
* Unparsed reference to the natural language contract that this code is supposed to express (usually a hash of * Unparsed reference to the natural language contract that this code is supposed to express (usually a hash of

View File

@ -122,12 +122,11 @@ sealed class TransactionType {
* If any contract fails to verify, the whole transaction is considered to be invalid. * If any contract fails to verify, the whole transaction is considered to be invalid.
*/ */
private fun verifyContracts(tx: LedgerTransaction) { private fun verifyContracts(tx: LedgerTransaction) {
val ctx = tx.toTransactionForContract()
// TODO: This will all be replaced in future once the sandbox and contract constraints work is done. // TODO: This will all be replaced in future once the sandbox and contract constraints work is done.
val contracts = (ctx.inputs.map { it.contract } + ctx.outputs.map { it.contract }).toSet() val contracts = (tx.inputs.map { it.state.data.contract } + tx.outputs.map { it.data.contract }).toSet()
for (contract in contracts) { for (contract in contracts) {
try { try {
contract.verify(ctx) contract.verify(tx)
} catch(e: Throwable) { } catch(e: Throwable) {
throw TransactionVerificationException.ContractRejection(tx.id, contract, e) throw TransactionVerificationException.ContractRejection(tx.id, contract, e)
} }

View File

@ -1,128 +0,0 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.NonEmptySet
import java.security.PublicKey
import java.util.*
// TODO: Consider moving this out of the core module and providing a different way for unit tests to test contracts.
/**
* A transaction to be passed as input to a contract verification function. Defines helper methods to
* simplify verification logic in contracts.
*/
// DOCSTART 1
data class TransactionForContract(val inputs: List<ContractState>,
val outputs: List<ContractState>,
val attachments: List<Attachment>,
val commands: List<AuthenticatedObject<CommandData>>,
val origHash: SecureHash,
val inputNotary: Party? = null,
val timeWindow: TimeWindow? = null) {
// DOCEND 1
override fun hashCode() = origHash.hashCode()
override fun equals(other: Any?) = other is TransactionForContract && other.origHash == origHash
/**
* Given a type and a function that returns a grouping key, associates inputs and outputs together so that they
* can be processed as one. The grouping key is any arbitrary object that can act as a map key (so must implement
* equals and hashCode).
*
* The purpose of this function is to simplify the writing of verification logic for transactions that may contain
* similar but unrelated state evolutions which need to be checked independently. Consider a transaction that
* simultaneously moves both dollars and euros (e.g. is an atomic FX trade). There may be multiple dollar inputs and
* multiple dollar outputs, depending on things like how fragmented the owner's vault is and whether various privacy
* techniques are in use. The quantity of dollars on the output side must sum to the same as on the input side, to
* ensure no money is being lost track of. This summation and checking must be repeated independently for each
* currency. To solve this, you would use groupStates with a type of Cash.State and a selector that returns the
* currency field: the resulting list can then be iterated over to perform the per-currency calculation.
*/
// DOCSTART 2
fun <T : ContractState, K : Any> groupStates(ofType: Class<T>, selector: (T) -> K): List<InOutGroup<T, K>> {
val inputs = inputs.filterIsInstance(ofType)
val outputs = outputs.filterIsInstance(ofType)
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
@Suppress("DEPRECATION")
return groupStatesInternal(inGroups, outGroups)
}
// DOCEND 2
/** See the documentation for the reflection-based version of [groupStates] */
inline fun <reified T : ContractState, K : Any> groupStates(selector: (T) -> K): List<InOutGroup<T, K>> {
val inputs = inputs.filterIsInstance<T>()
val outputs = outputs.filterIsInstance<T>()
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
@Suppress("DEPRECATION")
return groupStatesInternal(inGroups, outGroups)
}
@Deprecated("Do not use this directly: exposed as public only due to function inlining")
fun <T : ContractState, K : Any> groupStatesInternal(inGroups: Map<K, List<T>>, outGroups: Map<K, List<T>>): List<InOutGroup<T, K>> {
val result = ArrayList<InOutGroup<T, K>>()
for ((k, v) in inGroups.entries)
result.add(InOutGroup(v, outGroups[k] ?: emptyList(), k))
for ((k, v) in outGroups.entries) {
if (inGroups[k] == null)
result.add(InOutGroup(emptyList(), v, k))
}
return result
}
/** Utilities for contract writers to incorporate into their logic. */
/**
* A set of related inputs and outputs that are connected by some common attributes. An InOutGroup is calculated
* using [groupStates] and is useful for handling cases where a transaction may contain similar but unrelated
* state evolutions, for example, a transaction that moves cash in two different currencies. The numbers must add
* up on both sides of the transaction, but the values must be summed independently per currency. Grouping can
* be used to simplify this logic.
*/
// DOCSTART 3
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
// DOCEND 3
}
class TransactionResolutionException(val hash: SecureHash) : FlowException("Transaction resolution failure for $hash")
class AttachmentResolutionException(val hash: SecureHash) : FlowException("Attachment resolution failure for $hash")
sealed class TransactionVerificationException(val txId: SecureHash, message: String, cause: Throwable?)
: FlowException("$message, transaction: $txId", cause) {
class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable)
: TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause)
class MoreThanOneNotary(txId: SecureHash)
: TransactionVerificationException(txId, "More than one notary", null)
class SignersMissing(txId: SecureHash, missing: List<PublicKey>)
: TransactionVerificationException(txId, "Signers missing: ${missing.joinToString()}", null)
class DuplicateInputStates(txId: SecureHash, val duplicates: NonEmptySet<StateRef>)
: TransactionVerificationException(txId, "Duplicate inputs: ${duplicates.joinToString()}", null)
class InvalidNotaryChange(txId: SecureHash)
: TransactionVerificationException(txId, "Detected a notary change. Outputs must use the same notary as inputs", null)
class NotaryChangeInWrongTransactionType(txId: SecureHash, txNotary: Party, outputNotary: Party)
: TransactionVerificationException(txId, "Found unexpected notary change in transaction. Tx notary: $txNotary, found: $outputNotary", null)
class TransactionMissingEncumbranceException(txId: SecureHash, missing: Int, inOut: Direction)
: TransactionVerificationException(txId, "Missing required encumbrance $missing in $inOut", null)
@CordaSerializable
enum class Direction {
INPUT,
OUTPUT
}
}

View File

@ -0,0 +1,42 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.NonEmptySet
import java.security.PublicKey
class TransactionResolutionException(val hash: SecureHash) : FlowException("Transaction resolution failure for $hash")
class AttachmentResolutionException(val hash: SecureHash) : FlowException("Attachment resolution failure for $hash")
sealed class TransactionVerificationException(val txId: SecureHash, message: String, cause: Throwable?)
: FlowException("$message, transaction: $txId", cause) {
class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable)
: TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause)
class MoreThanOneNotary(txId: SecureHash)
: TransactionVerificationException(txId, "More than one notary", null)
class SignersMissing(txId: SecureHash, missing: List<PublicKey>)
: TransactionVerificationException(txId, "Signers missing: ${missing.joinToString()}", null)
class DuplicateInputStates(txId: SecureHash, val duplicates: NonEmptySet<StateRef>)
: TransactionVerificationException(txId, "Duplicate inputs: ${duplicates.joinToString()}", null)
class InvalidNotaryChange(txId: SecureHash)
: TransactionVerificationException(txId, "Detected a notary change. Outputs must use the same notary as inputs", null)
class NotaryChangeInWrongTransactionType(txId: SecureHash, txNotary: Party, outputNotary: Party)
: TransactionVerificationException(txId, "Found unexpected notary change in transaction. Tx notary: $txNotary, found: $outputNotary", null)
class TransactionMissingEncumbranceException(txId: SecureHash, missing: Int, inOut: Direction)
: TransactionVerificationException(txId, "Missing required encumbrance $missing in $inOut", null)
@CordaSerializable
enum class Direction {
INPUT,
OUTPUT
}
}

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import java.util.* import java.util.*
/** /**
@ -24,7 +24,7 @@ open class AllOf<S : ContractState, C : CommandData, K : Any>(firstClause: Claus
return clauses return clauses
} }
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import java.util.* import java.util.*
/** /**
@ -18,7 +18,7 @@ open class AnyOf<in S : ContractState, C : CommandData, in K : Any>(vararg rawCl
return matched return matched
} }
override fun verify(tx: TransactionForContract, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> { override fun verify(tx: LedgerTransaction, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> {
return matchedClauses(commands).flatMapTo(HashSet<C>()) { clause -> return matchedClauses(commands).flatMapTo(HashSet<C>()) { clause ->
clause.verify(tx, inputs, outputs, commands, groupingKey) clause.verify(tx, inputs, outputs, commands, groupingKey)
} }

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
/** /**
@ -48,14 +48,14 @@ abstract class Clause<in S : ContractState, C : CommandData, in K : Any> {
* @param commands commands which are relevant to this clause. By default this is the set passed into [verifyClause], * @param commands commands which are relevant to this clause. By default this is the set passed into [verifyClause],
* but may be further reduced by clauses such as [GroupClauseVerifier]. * but may be further reduced by clauses such as [GroupClauseVerifier].
* @param groupingKey a grouping key applied to states and commands, where applicable. Taken from * @param groupingKey a grouping key applied to states and commands, where applicable. Taken from
* [TransactionForContract.InOutGroup]. * [LedgerTransaction.InOutGroup].
* @return the set of commands that are consumed IF this clause is matched, and cannot be used to match a * @return the set of commands that are consumed IF this clause is matched, and cannot be used to match a
* later clause. This would normally be all commands matching "requiredCommands" for this clause, but some * later clause. This would normally be all commands matching "requiredCommands" for this clause, but some
* verify() functions may do further filtering on possible matches, and return a subset. This may also include * verify() functions may do further filtering on possible matches, and return a subset. This may also include
* commands that were not required (for example the Exit command for fungible assets is optional). * commands that were not required (for example the Exit command for fungible assets is optional).
*/ */
@Throws(IllegalStateException::class) @Throws(IllegalStateException::class)
abstract fun verify(tx: TransactionForContract, abstract fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -5,7 +5,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
/** /**
* Verify a transaction against the given list of clauses. * Verify a transaction against the given list of clauses.
@ -15,15 +15,15 @@ import net.corda.core.contracts.TransactionForContract
* @param commands commands extracted from the transaction, which are relevant to the * @param commands commands extracted from the transaction, which are relevant to the
* clauses. * clauses.
*/ */
fun <C : CommandData> verifyClause(tx: TransactionForContract, fun <C : CommandData> verifyClause(tx: LedgerTransaction,
clause: Clause<ContractState, C, Unit>, clause: Clause<ContractState, C, Unit>,
commands: List<AuthenticatedObject<C>>) { commands: List<AuthenticatedObject<C>>) {
if (Clause.log.isTraceEnabled) { if (Clause.log.isTraceEnabled) {
clause.getExecutionPath(commands).forEach { clause.getExecutionPath(commands).forEach {
Clause.log.trace("Tx ${tx.origHash} clause: $clause") Clause.log.trace("Tx ${tx.id} clause: $clause")
} }
} }
val matchedCommands = clause.verify(tx, tx.inputs, tx.outputs, commands, null) val matchedCommands = clause.verify(tx, tx.inputs.map { it.state.data }, tx.outputs.map { it.data }, commands, null)
check(matchedCommands.containsAll(commands.map { it.value })) { "The following commands were not matched at the end of execution: " + (commands - matchedCommands) } check(matchedCommands.containsAll(commands.map { it.value })) { "The following commands were not matched at the end of execution: " + (commands - matchedCommands) }
} }

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
/** /**
* Filter the states that are passed through to the wrapped clause, to restrict them to a specific type. * Filter the states that are passed through to the wrapped clause, to restrict them to a specific type.
@ -16,7 +16,7 @@ class FilterOn<S : ContractState, C : CommandData, in K : Any>(val clause: Claus
override fun getExecutionPath(commands: List<AuthenticatedObject<C>>): List<Clause<*, *, *>> override fun getExecutionPath(commands: List<AuthenticatedObject<C>>): List<Clause<*, *, *>>
= clause.getExecutionPath(commands) = clause.getExecutionPath(commands)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import java.util.* import java.util.*
/** /**
@ -19,7 +19,7 @@ class FirstComposition<S : ContractState, C : CommandData, K : Any>(firstClause:
clauses.addAll(remainingClauses) clauses.addAll(remainingClauses)
} }
override fun verify(tx: TransactionForContract, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> { override fun verify(tx: LedgerTransaction, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> {
val clause = matchedClauses(commands).singleOrNull() ?: throw IllegalStateException("No delegate clause matched in first composition") val clause = matchedClauses(commands).singleOrNull() ?: throw IllegalStateException("No delegate clause matched in first composition")
return clause.verify(tx, inputs, outputs, commands, groupingKey) return clause.verify(tx, inputs, outputs, commands, groupingKey)
} }

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import java.util.* import java.util.*
@ -33,7 +33,7 @@ class FirstOf<S : ContractState, C : CommandData, K : Any>(firstClause: Clause<S
clauses.addAll(remainingClauses) clauses.addAll(remainingClauses)
} }
override fun verify(tx: TransactionForContract, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> { override fun verify(tx: LedgerTransaction, inputs: List<S>, outputs: List<S>, commands: List<AuthenticatedObject<C>>, groupingKey: K?): Set<C> {
return matchedClause(commands).verify(tx, inputs, outputs, commands, groupingKey) return matchedClause(commands).verify(tx, inputs, outputs, commands, groupingKey)
} }

View File

@ -3,16 +3,16 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import java.util.* import java.util.*
abstract class GroupClauseVerifier<S : ContractState, C : CommandData, K : Any>(val clause: Clause<S, C, K>) : Clause<ContractState, C, Unit>() { abstract class GroupClauseVerifier<S : ContractState, C : CommandData, K : Any>(val clause: Clause<S, C, K>) : Clause<ContractState, C, Unit>() {
abstract fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<S, K>> abstract fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<S, K>>
override fun getExecutionPath(commands: List<AuthenticatedObject<C>>): List<Clause<*, *, *>> override fun getExecutionPath(commands: List<AuthenticatedObject<C>>): List<Clause<*, *, *>>
= clause.getExecutionPath(commands) = clause.getExecutionPath(commands)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -1,6 +1,7 @@
package net.corda.core.flows package net.corda.core.flows
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey import java.security.PublicKey
@ -21,9 +22,9 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
companion object { companion object {
@JvmStatic @JvmStatic
fun verify(tx: TransactionForContract) { fun verify(tx: LedgerTransaction) {
// Contract Upgrade transaction should have 1 input, 1 output and 1 command. // 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()) verify(tx.inputs.single().state.data, tx.outputs.single().data, tx.commands.map { Command(it.value, it.signers) }.single())
} }
@JvmStatic @JvmStatic

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey import java.security.PublicKey
import java.util.*
/** /**
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations: * A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
@ -19,6 +20,7 @@ import java.security.PublicKey
*/ */
// TODO LedgerTransaction is not supposed to be serialisable as it references attachments, etc. The verification logic // TODO LedgerTransaction is not supposed to be serialisable as it references attachments, etc. The verification logic
// currently sends this across to out-of-process verifiers. We'll need to change that first. // currently sends this across to out-of-process verifiers. We'll need to change that first.
// DOCSTART 1
@CordaSerializable @CordaSerializable
class LedgerTransaction( class LedgerTransaction(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */ /** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
@ -35,6 +37,7 @@ class LedgerTransaction(
timeWindow: TimeWindow?, timeWindow: TimeWindow?,
type: TransactionType type: TransactionType
) : BaseTransaction(inputs, outputs, notary, signers, type, timeWindow) { ) : BaseTransaction(inputs, outputs, notary, signers, type, timeWindow) {
//DOCEND 1
init { init {
checkInvariants() checkInvariants()
} }
@ -42,14 +45,6 @@ class LedgerTransaction(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T : ContractState> outRef(index: Int) = StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index)) fun <T : ContractState> outRef(index: Int) = StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index))
// TODO: Remove this concept.
// There isn't really a good justification for hiding this data from the contract, it's just a backwards compat hack.
/** Strips the transaction down to a form that is usable by the contract verify functions */
fun toTransactionForContract(): TransactionForContract {
return TransactionForContract(inputs.map { it.state.data }, outputs.map { it.data }, attachments, commands, id,
inputs.map { it.state.notary }.singleOrNull(), timeWindow)
}
/** /**
* Verifies this transaction and throws an exception if not valid, depending on the type. For general transactions: * Verifies this transaction and throws an exception if not valid, depending on the type. For general transactions:
* *
@ -88,4 +83,57 @@ class LedgerTransaction(
result = 31 * result + id.hashCode() result = 31 * result + id.hashCode()
return result return result
} }
/**
* Given a type and a function that returns a grouping key, associates inputs and outputs together so that they
* can be processed as one. The grouping key is any arbitrary object that can act as a map key (so must implement
* equals and hashCode).
*
* The purpose of this function is to simplify the writing of verification logic for transactions that may contain
* similar but unrelated state evolutions which need to be checked independently. Consider a transaction that
* simultaneously moves both dollars and euros (e.g. is an atomic FX trade). There may be multiple dollar inputs and
* multiple dollar outputs, depending on things like how fragmented the owner's vault is and whether various privacy
* techniques are in use. The quantity of dollars on the output side must sum to the same as on the input side, to
* ensure no money is being lost track of. This summation and checking must be repeated independently for each
* currency. To solve this, you would use groupStates with a type of Cash.State and a selector that returns the
* currency field: the resulting list can then be iterated over to perform the per-currency calculation.
*/
// DOCSTART 2
fun <T : ContractState, K : Any> groupStates(ofType: Class<T>, selector: (T) -> K): List<InOutGroup<T, K>> {
val inputs = inputs.map { it.state.data }.filterIsInstance(ofType)
val outputs = outputs.map { it.data }.filterIsInstance(ofType)
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
val result = ArrayList<InOutGroup<T, K>>()
for ((k, v) in inGroups.entries)
result.add(InOutGroup(v, outGroups[k] ?: emptyList(), k))
for ((k, v) in outGroups.entries) {
if (inGroups[k] == null)
result.add(InOutGroup(emptyList(), v, k))
}
return result
}
// DOCEND 2
/** See the documentation for the reflection-based version of [groupStates] */
inline fun <reified T : ContractState, K : Any> groupStates(noinline selector: (T) -> K): List<InOutGroup<T, K>> {
return groupStates(T::class.java, selector)
}
/** Utilities for contract writers to incorporate into their logic. */
/**
* A set of related inputs and outputs that are connected by some common attributes. An InOutGroup is calculated
* using [groupStates] and is useful for handling cases where a transaction may contain similar but unrelated
* state evolutions, for example, a transaction that moves cash in two different currencies. The numbers must add
* up on both sides of the transaction, but the values must be summed independently per currency. Grouping can
* be used to simplify this logic.
*/
// DOCSTART 3
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
// DOCEND 3
} }

View File

@ -3,6 +3,7 @@ package net.corda.core.contracts
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.ledger import net.corda.testing.ledger
@ -28,8 +29,8 @@ class TransactionEncumbranceTests {
class DummyTimeLock : Contract { class DummyTimeLock : Contract {
override val legalContractReference = SecureHash.sha256("DummyTimeLock") override val legalContractReference = SecureHash.sha256("DummyTimeLock")
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val timeLockInput = tx.inputs.filterIsInstance<State>().singleOrNull() ?: return val timeLockInput = tx.inputs.map { it.state.data }.filterIsInstance<State>().singleOrNull() ?: return
val time = tx.timeWindow?.untilTime ?: throw IllegalArgumentException("Transactions containing time-locks must have a time-window") val time = tx.timeWindow?.untilTime ?: throw IllegalArgumentException("Transactions containing time-locks must have a time-window")
requireThat { requireThat {
"the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom) "the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom)

View File

@ -2,8 +2,9 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import org.junit.Test import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -15,7 +16,7 @@ class AllOfTests {
fun minimal() { fun minimal() {
val counter = AtomicInteger(0) val counter = AtomicInteger(0)
val clause = AllOf(matchedClause(counter), matchedClause(counter)) val clause = AllOf(matchedClause(counter), matchedClause(counter))
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>())
// Check that we've run the verify() function of two clauses // Check that we've run the verify() function of two clauses
@ -25,7 +26,7 @@ class AllOfTests {
@Test @Test
fun `not all match`() { fun `not all match`() {
val clause = AllOf(matchedClause(), unmatchedClause()) val clause = AllOf(matchedClause(), unmatchedClause())
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
assertFailsWith<IllegalStateException> { verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) } assertFailsWith<IllegalStateException> { verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) }
} }
} }

View File

@ -2,8 +2,9 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import org.junit.Test import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -14,7 +15,7 @@ class AnyOfTests {
fun minimal() { fun minimal() {
val counter = AtomicInteger(0) val counter = AtomicInteger(0)
val clause = AnyOf(matchedClause(counter), matchedClause(counter)) val clause = AnyOf(matchedClause(counter), matchedClause(counter))
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>())
// Check that we've run the verify() function of two clauses // Check that we've run the verify() function of two clauses
@ -25,7 +26,7 @@ class AnyOfTests {
fun `not all match`() { fun `not all match`() {
val counter = AtomicInteger(0) val counter = AtomicInteger(0)
val clause = AnyOf(matchedClause(counter), unmatchedClause(counter)) val clause = AnyOf(matchedClause(counter), unmatchedClause(counter))
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>())
// Check that we've run the verify() function of one clause // Check that we've run the verify() function of one clause
@ -36,7 +37,7 @@ class AnyOfTests {
fun `none match`() { fun `none match`() {
val counter = AtomicInteger(0) val counter = AtomicInteger(0)
val clause = AnyOf(unmatchedClause(counter), unmatchedClause(counter)) val clause = AnyOf(unmatchedClause(counter), unmatchedClause(counter))
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
assertFailsWith(IllegalArgumentException::class) { assertFailsWith(IllegalArgumentException::class) {
verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>())
} }

View File

@ -3,12 +3,12 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.transactions.LedgerTransaction
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
internal fun matchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() { internal fun matchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() {
override val requiredCommands: Set<Class<out CommandData>> = emptySet() override val requiredCommands: Set<Class<out CommandData>> = emptySet()
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> { commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> {
@ -20,7 +20,7 @@ internal fun matchedClause(counter: AtomicInteger? = null) = object : Clause<Con
/** A clause that can never be matched */ /** A clause that can never be matched */
internal fun unmatchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() { internal fun unmatchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(object : CommandData {}.javaClass) override val requiredCommands: Set<Class<out CommandData>> = setOf(object : CommandData {}.javaClass)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> { commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> {

View File

@ -3,9 +3,10 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionType
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.contracts.DummyContract
import org.junit.Test import org.junit.Test
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -17,25 +18,25 @@ class VerifyClausesTests {
@Test @Test
fun minimal() { fun minimal() {
val clause = object : Clause<ContractState, CommandData, Unit>() { val clause = object : Clause<ContractState, CommandData, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> = emptySet() commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> = emptySet()
} }
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), emptyList(), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>()) verifyClause(tx, clause, emptyList<AuthenticatedObject<CommandData>>())
} }
@Test @Test
fun errorSuperfluousCommands() { fun errorSuperfluousCommands() {
val clause = object : Clause<ContractState, CommandData, Unit>() { val clause = object : Clause<ContractState, CommandData, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> = emptySet() commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> = emptySet()
} }
val command = AuthenticatedObject(emptyList(), emptyList(), DummyContract.Commands.Create()) val command = AuthenticatedObject(emptyList(), emptyList(), DummyContract.Commands.Create())
val tx = TransactionForContract(emptyList(), emptyList(), emptyList(), listOf(command), SecureHash.randomSHA256()) val tx = LedgerTransaction(emptyList(), emptyList(), listOf(command), emptyList(), SecureHash.randomSHA256(), null, emptyList(), null, TransactionType.General)
// The clause is matched, but doesn't mark the command as consumed, so this should error // The clause is matched, but doesn't mark the command as consumed, so this should error
assertFailsWith<IllegalStateException> { verifyClause(tx, clause, listOf(command)) } assertFailsWith<IllegalStateException> { verifyClause(tx, clause, listOf(command)) }
} }

View File

@ -10,6 +10,7 @@ import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -206,7 +207,7 @@ class ContractUpgradeFlowTest {
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner)) override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))
override fun verify(tx: TransactionForContract) {} override fun verify(tx: LedgerTransaction) {}
// Dummy Cash contract for testing. // Dummy Cash contract for testing.
override val legalContractReference = SecureHash.sha256("") override val legalContractReference = SecureHash.sha256("")

View File

@ -9,6 +9,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
@ -58,7 +59,7 @@ class AttachmentClassLoaderTests {
class Create : TypeOnlyCommandData(), Commands class Create : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Always accepts. // Always accepts.
} }

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -13,7 +14,7 @@ class VaultUpdateTests {
object DummyContract : Contract { object DummyContract : Contract {
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
} }
override val legalContractReference: SecureHash = SecureHash.sha256("") override val legalContractReference: SecureHash = SecureHash.sha256("")

View File

@ -3,8 +3,9 @@ package net.corda.core.serialization
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.utilities.seconds import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.seconds
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import org.junit.Before import org.junit.Before
@ -20,7 +21,7 @@ class TransactionSerializationTests {
class TestCash : Contract { class TestCash : Contract {
override val legalContractReference = SecureHash.sha256("TestCash") override val legalContractReference = SecureHash.sha256("TestCash")
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
} }
data class State( data class State(

View File

@ -1,6 +1,10 @@
package net.corda.core.serialization.amqp package net.corda.core.serialization.amqp
import net.corda.core.contracts.* import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -8,7 +12,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.EmptyWhitelist import net.corda.core.serialization.EmptyWhitelist
import net.corda.core.serialization.KryoAMQPSerializer import net.corda.core.serialization.KryoAMQPSerializer
import net.corda.core.serialization.amqp.SerializerFactory.Companion.isPrimitive import net.corda.core.serialization.amqp.SerializerFactory.Companion.isPrimitive
import net.corda.core.CordaRuntimeException import net.corda.core.transactions.LedgerTransaction
import net.corda.nodeapi.RPCException import net.corda.nodeapi.RPCException
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY import net.corda.testing.MEGA_CORP_PUBKEY
@ -505,7 +509,7 @@ class SerializationOutputTests {
} }
object FooContract : Contract { object FooContract : Contract {
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
} }

View File

@ -22,19 +22,19 @@ The ``Contract`` interface is defined as follows:
Where: Where:
* ``verify(tx: TransactionForContract)`` determines whether transactions involving states which reference this * ``verify(tx: LedgerTransaction)`` determines whether transactions involving states which reference this
contract type are valid contract type are valid
* ``legalContractReference`` is the hash of the legal prose contract that ``verify`` seeks to express in code * ``legalContractReference`` is the hash of the legal prose contract that ``verify`` seeks to express in code
verify() verify()
-------- --------
``verify()`` is a method that doesn't return anything and takes a ``TransactionForContract`` as a parameter. It ``verify()`` is a method that doesn't return anything and takes a ``LedgerTransaction`` as a parameter. It
either throws an exception if the transaction is considered invalid, or returns normally if the transaction is either throws an exception if the transaction is considered invalid, or returns normally if the transaction is
considered valid. considered valid.
``verify()`` is executed in a sandbox. It does not have access to the enclosing scope, and is not able to access ``verify()`` is executed in a sandbox. It does not have access to the enclosing scope, and is not able to access
the network or perform any other I/O. It only has access to the properties defined on ``TransactionForContract`` when the network or perform any other I/O. It only has access to the properties defined on ``LedgerTransaction`` when
establishing whether a transaction is valid. establishing whether a transaction is valid.
The two simplest ``verify`` functions are the one that accepts all transactions, and the one that rejects all The two simplest ``verify`` functions are the one that accepts all transactions, and the one that rejects all
@ -46,14 +46,14 @@ Here is the ``verify`` that accepts all transactions:
.. sourcecode:: kotlin .. sourcecode:: kotlin
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Always accepts! // Always accepts!
} }
.. sourcecode:: java .. sourcecode:: java
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
// Always accepts! // Always accepts!
} }
@ -63,39 +63,40 @@ And here is the ``verify`` that rejects all transactions:
.. sourcecode:: kotlin .. sourcecode:: kotlin
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
throw IllegalArgumentException("Always rejects!") throw IllegalArgumentException("Always rejects!")
} }
.. sourcecode:: java .. sourcecode:: java
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
throw new IllegalArgumentException("Always rejects!"); throw new IllegalArgumentException("Always rejects!");
} }
TransactionForContract LedgerTransaction
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
The ``TransactionForContract`` object passed into ``verify()`` represents the full set of information available to The ``LedgerTransaction`` object passed into ``verify()`` represents the full set of information available to
``verify()`` when deciding whether to accept or reject the transaction. It has the following properties: ``verify()`` when deciding whether to accept or reject the transaction. It has the following properties:
.. container:: codeset .. container:: codeset
.. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt
:language: kotlin :language: kotlin
:start-after: DOCSTART 1 :start-after: DOCSTART 1
:end-before: DOCEND 1 :end-before: DOCEND 1
Where: Where:
* ``inputs`` is a list of the transaction's inputs * ``inputs`` is a list of the transaction's inputs'
* ``outputs`` is a list of the transaction's outputs * ``outputs`` is a list of the transaction's outputs'
* ``attachments`` is a list of the transaction's attachments * ``attachments`` is a list of the transaction's attachments'
* ``commands`` is a list of the transaction's commands, and their associated signatures * ``commands`` is a list of the transaction's commands, and their associated signatures'
* ``origHash`` is the transaction's hash * ``id`` is the transaction's merkle root hash'
* ``inputNotary`` is the transaction's notary * ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
* ``timestamp`` is the transaction's timestamp * ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation.
* ``type`` is the class of Transaction. Normal ledger data transactions are ``TransactionType.General``, but migration of states to a new notary uses ``TransactionType.NotaryChange``.
requireThat() requireThat()
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@ -134,7 +135,7 @@ exception will cause the transaction to be rejected.
Commands Commands
^^^^^^^^ ^^^^^^^^
``TransactionForContract`` contains the commands as a list of ``AuthenticatedObject`` instances. ``LedgerTransaction`` contains the commands as a list of ``AuthenticatedObject`` instances.
``AuthenticatedObject`` pairs an object with a list of signers. In this case, ``AuthenticatedObject`` pairs a command ``AuthenticatedObject`` pairs an object with a list of signers. In this case, ``AuthenticatedObject`` pairs a command
with a list of the entities that are required to sign a transaction where this command is present: with a list of the entities that are required to sign a transaction where this command is present:
@ -175,7 +176,7 @@ execution of ``verify()``:
class Transfer : TypeOnlyCommandData(), Commands class Transfer : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>() val command = tx.commands.requireSingleCommand<Commands>()
when (command.value) { when (command.value) {
@ -200,7 +201,7 @@ execution of ``verify()``:
} }
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
final AuthenticatedObject<Commands> command = requireSingleCommand(tx.getCommands(), Commands.class); final AuthenticatedObject<Commands> command = requireSingleCommand(tx.getCommands(), Commands.class);
if (command.getValue() instanceof Commands.Issue) { if (command.getValue() instanceof Commands.Issue) {
@ -228,7 +229,7 @@ We can imagine that we would like to verify the USD states and the GBP states se
:scale: 20 :scale: 20
:align: center :align: center
``TransactionForContract`` provides a ``groupStates`` method to allow you to group states in this way: ``LedgerTransaction`` provides a ``groupStates`` method to allow you to group states in this way:
.. container:: codeset .. container:: codeset

View File

@ -38,6 +38,13 @@ UNRELEASED
overriden in order to handle cases where no single transaction participant is aware of all parties, and therefore overriden in order to handle cases where no single transaction participant is aware of all parties, and therefore
the transaction must be relayed between participants rather than sent from a single node. the transaction must be relayed between participants rather than sent from a single node.
* ``TransactionForContract`` has been removed and all usages of this class have been replaced with usage of
``LedgerTransaction``. In particular ``Contract.verify`` and the ``Clauses`` API have been changed and now take a
``LedgerTransaction`` as passed in parameter. The prinicpal consequence of this is that the types of the input and output
collections on the transaction object have changed, so it may be necessary to ``map`` down to the ``ContractState``
sub-properties in existing code.
It is intended that new helper methods will be added shortly to the API to reduce the impact of these changes.
Milestone 13 Milestone 13
------------ ------------

View File

@ -18,7 +18,7 @@ Let's take a look at a simplified structure of the ``Clause`` class:
open val requiredCommands: Set<Class<out CommandData>> = emptySet() open val requiredCommands: Set<Class<out CommandData>> = emptySet()
@Throws(IllegalStateException::class) @Throws(IllegalStateException::class)
abstract fun verify(tx: TransactionForContract, abstract fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,
@ -47,7 +47,7 @@ An example ``verify`` from ``Obligation`` contract:
.. sourcecode:: kotlin .. sourcecode:: kotlin
override fun verify(tx: TransactionForContract) = verifyClause<Commands>(tx, FirstOf<ContractState, Commands, Unit>( override fun verify(tx: LedgerTransaction) = verifyClause<Commands>(tx, FirstOf<ContractState, Commands, Unit>(
Clauses.Net<Commands, P>(), Clauses.Net<Commands, P>(),
Clauses.Group<P>() Clauses.Group<P>()
), tx.commands.select<Obligation.Commands>()) ), tx.commands.select<Obligation.Commands>())
@ -112,7 +112,7 @@ Example from ``CommercialPaper.kt``:
Redeem(), Redeem(),
Move(), Move(),
Issue())) { Issue())) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Terms>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Terms>>>
= tx.groupStates<State, Issued<Terms>> { it.token } = tx.groupStates<State, Issued<Terms>> { it.token }
} }
@ -183,11 +183,11 @@ grouped input and output states with a grouping key used for each group. Example
) )
) )
) { ) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>>
= tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.amount.token } = tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.amount.token }
} }
Usually it's convenient to use ``groupStates`` function defined on ``TransactionForContract`` class. Which given a type and a Usually it's convenient to use ``groupStates`` function defined on ``LedgerTransaction`` class. Which given a type and a
selector function, that returns a grouping key, associates inputs and outputs together so that they can be processed as one. selector function, that returns a grouping key, associates inputs and outputs together so that they can be processed as one.
The grouping key is any arbitrary object that can act as a map key (so must implement equals and hashCode). The grouping key is any arbitrary object that can act as a map key (so must implement equals and hashCode).
@ -232,7 +232,7 @@ Example from ``CommercialPaper.kt``:
{ token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) { { token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,

View File

@ -14,6 +14,7 @@ import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.linearHeadsOfType import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
@ -69,7 +70,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
* The verify method locks down the allowed transactions to contain just a single proposal being * The verify method locks down the allowed transactions to contain just a single proposal being
* created/modified and the only modification allowed is to the state field. * created/modified and the only modification allowed is to the state field.
*/ */
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<TradeApprovalContract.Commands>() val command = tx.commands.requireSingleCommand<TradeApprovalContract.Commands>()
require(tx.timeWindow?.midpoint != null) { "must have a time-window" } require(tx.timeWindow?.midpoint != null) { "must have a time-window" }
when (command.value) { when (command.value) {
@ -78,7 +79,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
"Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty()) "Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty())
"Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1) "Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1)
} }
val issued = tx.outputs[0] as TradeApprovalContract.State val issued = tx.outputs[0].data as TradeApprovalContract.State
requireThat { requireThat {
"Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey)) "Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey))
"Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW) "Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW)

View File

@ -36,7 +36,7 @@ Just as every Corda state must implement the ``ContractState`` interface, every
interface Contract { interface Contract {
// Implements the contract constraints in code. // Implements the contract constraints in code.
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun verify(tx: TransactionForContract) fun verify(tx: LedgerTransaction)
// Expresses the contract constraints as legal prose. // Expresses the contract constraints as legal prose.
val legalContractReference: SecureHash val legalContractReference: SecureHash
@ -94,7 +94,7 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
// Our Create command. // Our Create command.
class Create : CommandData class Create : CommandData
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>() val command = tx.commands.requireSingleCommand<Create>()
requireThat { requireThat {
@ -103,7 +103,7 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
"There should be one output state of type IOUState." using (tx.outputs.size == 1) "There should be one output state of type IOUState." using (tx.outputs.size == 1)
// IOU-specific constraints. // IOU-specific constraints.
val out = tx.outputs.single() as IOUState val out = tx.outputs.single().data as IOUState
"The IOU's value must be non-negative." using (out.value > 0) "The IOU's value must be non-negative." using (out.value > 0)
"The lender and the borrower cannot be the same entity." using (out.lender != out.borrower) "The lender and the borrower cannot be the same entity." using (out.lender != out.borrower)
@ -125,7 +125,7 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
import net.corda.core.contracts.AuthenticatedObject; import net.corda.core.contracts.AuthenticatedObject;
import net.corda.core.contracts.CommandData; import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract; import net.corda.core.contracts.Contract;
import net.corda.core.contracts.TransactionForContract; import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.crypto.SecureHash; import net.corda.core.crypto.SecureHash;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
@ -137,7 +137,7 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
public static class Create implements CommandData {} public static class Create implements CommandData {}
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
final AuthenticatedObject<Create> command = requireSingleCommand(tx.getCommands(), Create.class); final AuthenticatedObject<Create> command = requireSingleCommand(tx.getCommands(), Create.class);
requireThat(check -> { requireThat(check -> {
@ -146,7 +146,7 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1); check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
// IOU-specific constraints. // IOU-specific constraints.
final IOUState out = (IOUState) tx.getOutputs().get(0); final IOUState out = (IOUState) tx.getOutputs().getData().get(0);
final Party lender = out.getLender(); final Party lender = out.getLender();
final Party borrower = out.getBorrower(); final Party borrower = out.getBorrower();
check.using("The IOU's value must be non-negative.",out.getValue() > 0); check.using("The IOU's value must be non-negative.",out.getValue() > 0);

View File

@ -68,7 +68,7 @@ We start by defining the ``CommercialPaper`` class. As in the previous tutorial,
class CommercialPaper : Contract { class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper") override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
override fun verify(tx: TransactionForContract) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>()) override fun verify(tx: LedgerTransaction) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>())
interface Commands : CommandData { interface Commands : CommandData {
data class Move(override val contractHash: SecureHash? = null) : FungibleAsset.Commands.Move, Commands data class Move(override val contractHash: SecureHash? = null) : FungibleAsset.Commands.Move, Commands
@ -85,7 +85,7 @@ We start by defining the ``CommercialPaper`` class. As in the previous tutorial,
} }
@Override @Override
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException { public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx)); ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx));
} }
@ -128,7 +128,7 @@ and is included in the ``CommercialPaper.kt`` code.
override val requiredCommands: Set<Class<out CommandData>> override val requiredCommands: Set<Class<out CommandData>>
get() = setOf(Commands.Move::class.java) get() = setOf(Commands.Move::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -158,7 +158,7 @@ and is included in the ``CommercialPaper.kt`` code.
@NotNull @NotNull
@Override @Override
public Set<Commands> verify(@NotNull TransactionForContract tx, public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@ -229,7 +229,7 @@ its subclauses (wrapped move, issue, redeem). "Any" in this case means that it w
Redeem(), Redeem(),
Move(), Move(),
Issue())) { Issue())) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Terms>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Terms>>>
= tx.groupStates<State, Issued<Terms>> { it.token } = tx.groupStates<State, Issued<Terms>> { it.token }
} }
@ -246,7 +246,7 @@ its subclauses (wrapped move, issue, redeem). "Any" in this case means that it w
@NotNull @NotNull
@Override @Override
public List<InOutGroup<State, State>> groupStates(@NotNull TransactionForContract tx) { public List<InOutGroup<State, State>> groupStates(@NotNull LedgerTransaction tx) {
return tx.groupStates(State.class, State::withoutOwner); return tx.groupStates(State.class, State::withoutOwner);
} }
} }

View File

@ -61,7 +61,7 @@ Kotlin syntax works.
class CommercialPaper : Contract { class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper"); override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
TODO() TODO()
} }
} }
@ -75,7 +75,7 @@ Kotlin syntax works.
} }
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
@ -298,7 +298,7 @@ run two contracts one time each: Cash and CommercialPaper.
.. sourcecode:: kotlin .. sourcecode:: kotlin
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Group by everything except owner: any modification to the CP at all is considered changing it fundamentally. // Group by everything except owner: any modification to the CP at all is considered changing it fundamentally.
val groups = tx.groupStates(State::withoutOwner) val groups = tx.groupStates(State::withoutOwner)
@ -309,7 +309,7 @@ run two contracts one time each: Cash and CommercialPaper.
.. sourcecode:: java .. sourcecode:: java
@Override @Override
public void verify(TransactionForContract tx) { public void verify(LedgerTransaction tx) {
List<InOutGroup<State, State>> groups = tx.groupStates(State.class, State::withoutOwner); List<InOutGroup<State, State>> groups = tx.groupStates(State.class, State::withoutOwner);
AuthenticatedObject<Command> cmd = requireSingleCommand(tx.getCommands(), Commands.class); AuthenticatedObject<Command> cmd = requireSingleCommand(tx.getCommands(), Commands.class);
@ -356,7 +356,7 @@ inputs e.g. because she received the dollars in two payments. The input and outp
the cash smart contract must consider the pounds and the dollars separately because they are not fungible: they cannot the cash smart contract must consider the pounds and the dollars separately because they are not fungible: they cannot
be merged together. So we have two groups: A and B. be merged together. So we have two groups: A and B.
The ``TransactionForContract.groupStates`` method handles this logic for us: firstly, it selects only states of the The ``LedgerTransaction.groupStates`` method handles this logic for us: firstly, it selects only states of the
given type (as the transaction may include other types of state, such as states representing bond ownership, or a given type (as the transaction may include other types of state, such as states representing bond ownership, or a
multi-sig state) and then it takes a function that maps a state to a grouping key. All states that share the same key are multi-sig state) and then it takes a function that maps a state to a grouping key. All states that share the same key are
grouped together. In the case of the cash example above, the grouping key would be the currency. grouped together. In the case of the cash example above, the grouping key would be the currency.
@ -791,7 +791,7 @@ The time-lock contract mentioned above can be implemented very simply:
class TestTimeLock : Contract { class TestTimeLock : Contract {
... ...
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val time = tx.timestamp.before ?: throw IllegalStateException(...) val time = tx.timestamp.before ?: throw IllegalStateException(...)
... ...
requireThat { requireThat {

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
@ -37,14 +38,14 @@ class UniversalContract : Contract {
class Split(val ratio: BigDecimal) : Commands class Split(val ratio: BigDecimal) : Commands
} }
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant? = when (expr) { fun eval(@Suppress("UNUSED_PARAMETER") tx: LedgerTransaction, expr: Perceivable<Instant>): Instant? = when (expr) {
is Const -> expr.value is Const -> expr.value
is StartDate -> null is StartDate -> null
is EndDate -> null is EndDate -> null
else -> throw Error("Unable to evaluate") else -> throw Error("Unable to evaluate")
} }
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) { fun eval(tx: LedgerTransaction, expr: Perceivable<Boolean>): Boolean = when (expr) {
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right) is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
is PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right) is PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right)
is Const<Boolean> -> expr.value is Const<Boolean> -> expr.value
@ -57,7 +58,7 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name) else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
} }
fun eval(tx: TransactionForContract, expr: Perceivable<BigDecimal>): BigDecimal = fun eval(tx: LedgerTransaction, expr: Perceivable<BigDecimal>): BigDecimal =
when (expr) { when (expr) {
is Const<BigDecimal> -> expr.value is Const<BigDecimal> -> expr.value
is UnaryPlus -> { is UnaryPlus -> {
@ -94,7 +95,7 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("eval - BigDecimal - " + expr.javaClass.name) else -> throw NotImplementedError("eval - BigDecimal - " + expr.javaClass.name)
} }
fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) { fun validateImmediateTransfers(tx: LedgerTransaction, arrangement: Arrangement): Arrangement = when (arrangement) {
is Obligation -> { is Obligation -> {
val amount = eval(tx, arrangement.amount) val amount = eval(tx, arrangement.amount)
requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) } requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) }
@ -179,7 +180,7 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name) else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name)
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
requireThat { requireThat {
"transaction has a single command".using(tx.commands.size == 1) "transaction has a single command".using(tx.commands.size == 1)
@ -191,7 +192,7 @@ class UniversalContract : Contract {
when (value) { when (value) {
is Commands.Action -> { is Commands.Action -> {
val inState = tx.inputs.single() as State val inState = tx.inputs.single().state.data as State
val arr = when (inState.details) { val arr = when (inState.details) {
is Actions -> inState.details is Actions -> inState.details
is RollOut -> reduceRollOut(inState.details) is RollOut -> reduceRollOut(inState.details)
@ -221,7 +222,7 @@ class UniversalContract : Contract {
when (tx.outputs.size) { when (tx.outputs.size) {
1 -> { 1 -> {
val outState = tx.outputs.single() as State val outState = tx.outputs.single().data as State
requireThat { requireThat {
"output state must match action result state" using (arrangement.equals(outState.details)) "output state must match action result state" using (arrangement.equals(outState.details))
"output state must match action result state" using (rest == zero) "output state must match action result state" using (rest == zero)
@ -229,7 +230,7 @@ class UniversalContract : Contract {
} }
0 -> throw IllegalArgumentException("must have at least one out state") 0 -> throw IllegalArgumentException("must have at least one out state")
else -> { else -> {
val allContracts = And(tx.outputs.map { (it as State).details }.toSet()) val allContracts = And(tx.outputs.map { (it.data as State).details }.toSet())
requireThat { requireThat {
"output states must match action result state" using (arrangement.equals(allContracts)) "output states must match action result state" using (arrangement.equals(allContracts))
@ -239,15 +240,15 @@ class UniversalContract : Contract {
} }
} }
is Commands.Issue -> { is Commands.Issue -> {
val outState = tx.outputs.single() as State val outState = tx.outputs.single().data as State
requireThat { requireThat {
"the transaction is signed by all liable parties" using (liableParties(outState.details).all { it in cmd.signers }) "the transaction is signed by all liable parties" using (liableParties(outState.details).all { it in cmd.signers })
"the transaction has no input states" using tx.inputs.isEmpty() "the transaction has no input states" using tx.inputs.isEmpty()
} }
} }
is Commands.Move -> { is Commands.Move -> {
val inState = tx.inputs.single() as State val inState = tx.inputs.single().state.data as State
val outState = tx.outputs.single() as State val outState = tx.outputs.single().data as State
requireThat { requireThat {
"the transaction is signed by all liable parties" using "the transaction is signed by all liable parties" using
(liableParties(outState.details).all { it in cmd.signers }) (liableParties(outState.details).all { it in cmd.signers })
@ -256,13 +257,13 @@ class UniversalContract : Contract {
} }
} }
is Commands.Fix -> { is Commands.Fix -> {
val inState = tx.inputs.single() as State val inState = tx.inputs.single().state.data as State
val arr = when (inState.details) { val arr = when (inState.details) {
is Actions -> inState.details is Actions -> inState.details
is RollOut -> reduceRollOut(inState.details) is RollOut -> reduceRollOut(inState.details)
else -> throw IllegalArgumentException("Unexpected arrangement, " + tx.inputs.single()) else -> throw IllegalArgumentException("Unexpected arrangement, " + tx.inputs.single())
} }
val outState = tx.outputs.single() as State val outState = tx.outputs.single().data as State
val unusedFixes = value.fixes.map { it.of }.toMutableSet() val unusedFixes = value.fixes.map { it.of }.toMutableSet()
val expectedArr = replaceFixing(tx, arr, val expectedArr = replaceFixing(tx, arr,
@ -279,7 +280,7 @@ class UniversalContract : Contract {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>, fun <T> replaceFixing(tx: LedgerTransaction, perceivable: Perceivable<T>,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> = fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
when (perceivable) { when (perceivable) {
is Const -> perceivable is Const -> perceivable
@ -299,11 +300,11 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name) else -> throw NotImplementedError("replaceFixing - " + perceivable.javaClass.name)
} }
fun replaceFixing(tx: TransactionForContract, arr: Action, fun replaceFixing(tx: LedgerTransaction, arr: Action,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) = fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) =
Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings), replaceFixing(tx, arr.arrangement, fixings, unusedFixings)) Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings), replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
fun replaceFixing(tx: TransactionForContract, arr: Arrangement, fun replaceFixing(tx: LedgerTransaction, arr: Arrangement,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement = fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement =
when (arr) { when (arr) {
is Zero -> arr is Zero -> arr

View File

@ -1,11 +1,11 @@
package net.corda.contracts.isolated package net.corda.contracts.isolated
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
@ -22,7 +22,7 @@ class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor
class Create : TypeOnlyCommandData(), Commands class Create : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Always accepts. // Always accepts.
} }

View File

@ -7,7 +7,6 @@ import kotlin.Pair;
import kotlin.Unit; import kotlin.Unit;
import net.corda.contracts.asset.CashKt; import net.corda.contracts.asset.CashKt;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.contracts.TransactionForContract.InOutGroup;
import net.corda.core.contracts.clauses.AnyOf; import net.corda.core.contracts.clauses.AnyOf;
import net.corda.core.contracts.clauses.Clause; import net.corda.core.contracts.clauses.Clause;
import net.corda.core.contracts.clauses.ClauseVerifier; import net.corda.core.contracts.clauses.ClauseVerifier;
@ -18,6 +17,7 @@ import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty; import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.node.services.VaultService; import net.corda.core.node.services.VaultService;
import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.transactions.TransactionBuilder; import net.corda.core.transactions.TransactionBuilder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -153,7 +153,7 @@ public class JavaCommercialPaper implements Contract {
@NotNull @NotNull
@Override @Override
public List<InOutGroup<State, State>> groupStates(@NotNull TransactionForContract tx) { public List<LedgerTransaction.InOutGroup<State, State>> groupStates(@NotNull LedgerTransaction tx) {
return tx.groupStates(State.class, State::withoutOwner); return tx.groupStates(State.class, State::withoutOwner);
} }
} }
@ -168,11 +168,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull @NotNull
@Override @Override
public Set<Commands> verify(@NotNull TransactionForContract tx, public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class); AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
// There should be only a single input due to aggregation above // There should be only a single input due to aggregation above
State input = Iterables.getOnlyElement(inputs); State input = Iterables.getOnlyElement(inputs);
@ -200,11 +200,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull @NotNull
@Override @Override
public Set<Commands> verify(@NotNull TransactionForContract tx, public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class); AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
// There should be only a single input due to aggregation above // There should be only a single input due to aggregation above
@ -217,7 +217,7 @@ public class JavaCommercialPaper implements Contract {
Instant time = null == timeWindow Instant time = null == timeWindow
? null ? null
: timeWindow.getUntilTime(); : timeWindow.getUntilTime();
Amount<Issued<Currency>> received = CashKt.sumCashBy(tx.getOutputs(), input.getOwner()); Amount<Issued<Currency>> received = CashKt.sumCashBy(tx.getOutputs().stream().map(TransactionState::getData).collect(Collectors.toList()), input.getOwner());
requireThat(require -> { requireThat(require -> {
require.using("must be timestamped", timeWindow != null); require.using("must be timestamped", timeWindow != null);
@ -243,11 +243,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull @NotNull
@Override @Override
public Set<Commands> verify(@NotNull TransactionForContract tx, public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class); AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
State output = Iterables.getOnlyElement(outputs); State output = Iterables.getOnlyElement(outputs);
TimeWindow timeWindowCommand = tx.getTimeWindow(); TimeWindow timeWindowCommand = tx.getTimeWindow();
@ -293,7 +293,7 @@ public class JavaCommercialPaper implements Contract {
} }
@NotNull @NotNull
private List<AuthenticatedObject<Commands>> extractCommands(@NotNull TransactionForContract tx) { private List<AuthenticatedObject<Commands>> extractCommands(@NotNull LedgerTransaction tx) {
return tx.getCommands() return tx.getCommands()
.stream() .stream()
.filter((AuthenticatedObject<CommandData> command) -> command.getValue() instanceof Commands) .filter((AuthenticatedObject<CommandData> command) -> command.getValue() instanceof Commands)
@ -302,7 +302,7 @@ public class JavaCommercialPaper implements Contract {
} }
@Override @Override
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException { public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx)); ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx));
} }

View File

@ -9,14 +9,15 @@ import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.GroupClauseVerifier import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.crypto.random63BitValue
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.schemas.CommercialPaperSchemaV1 import net.corda.schemas.CommercialPaperSchemaV1
@ -57,7 +58,7 @@ class CommercialPaper : Contract {
val maturityDate: Instant val maturityDate: Instant
) )
override fun verify(tx: TransactionForContract) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>()) override fun verify(tx: LedgerTransaction) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>())
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
@ -112,7 +113,7 @@ class CommercialPaper : Contract {
Redeem(), Redeem(),
Move(), Move(),
Issue())) { Issue())) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Terms>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Terms>>>
= tx.groupStates<State, Issued<Terms>> { it.token } = tx.groupStates<State, Issued<Terms>> { it.token }
} }
@ -121,7 +122,7 @@ class CommercialPaper : Contract {
{ token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) { { token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -140,7 +141,7 @@ class CommercialPaper : Contract {
class Move : Clause<State, Commands, Issued<Terms>>() { class Move : Clause<State, Commands, Issued<Terms>>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Move::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Move::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -160,7 +161,7 @@ class CommercialPaper : Contract {
class Redeem : Clause<State, Commands, Issued<Terms>>() { class Redeem : Clause<State, Commands, Issued<Terms>>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Redeem::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Redeem::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -171,7 +172,7 @@ class CommercialPaper : Contract {
val timeWindow = tx.timeWindow val timeWindow = tx.timeWindow
val input = inputs.single() val input = inputs.single()
val received = tx.outputs.sumCashBy(input.owner) val received = tx.outputs.map { it.data }.sumCashBy(input.owner)
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window") val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
requireThat { requireThat {
"the paper must have matured" using (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.testing.NULL_PARTY
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import java.time.Instant import java.time.Instant
@ -54,7 +55,7 @@ class CommercialPaperLegacy : Contract {
class Issue : TypeOnlyCommandData(), Commands class Issue : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Group by everything except owner: any modification to the CP at all is considered changing it fundamentally. // Group by everything except owner: any modification to the CP at all is considered changing it fundamentally.
val groups = tx.groupStates(State::withoutOwner) val groups = tx.groupStates(State::withoutOwner)
@ -80,7 +81,7 @@ class CommercialPaperLegacy : Contract {
is Commands.Redeem -> { is Commands.Redeem -> {
// Redemption of the paper requires movement of on-ledger cash. // Redemption of the paper requires movement of on-ledger cash.
val input = inputs.single() val input = inputs.single()
val received = tx.outputs.sumCashBy(input.owner) val received = tx.outputs.map { it.data }.sumCashBy(input.owner)
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window") val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
requireThat { requireThat {
"the paper must have matured" using (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)

View File

@ -19,6 +19,7 @@ import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.schemas.CashSchemaV1 import net.corda.schemas.CashSchemaV1
@ -72,7 +73,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
ConserveAmount()) ConserveAmount())
) )
) { ) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Currency>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Currency>>>
= tx.groupStates<State, Issued<Currency>> { it.amount.token } = tx.groupStates<State, Issued<Currency>> { it.amount.token }
} }
@ -173,7 +174,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override fun generateIssueCommand() = Commands.Issue() override fun generateIssueCommand() = Commands.Issue()
override fun generateMoveCommand() = Commands.Move() override fun generateMoveCommand() = Commands.Move()
override fun verify(tx: TransactionForContract) override fun verify(tx: LedgerTransaction)
= verifyClause(tx, Clauses.Group(), extractCommands(tx.commands)) = verifyClause(tx, Clauses.Group(), extractCommands(tx.commands))
} }

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.util.* import java.util.*
@ -69,7 +70,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/** /**
* Group commodity states by issuance definition (issuer and underlying commodity). * Group commodity states by issuance definition (issuer and underlying commodity).
*/ */
override fun groupStates(tx: TransactionForContract) override fun groupStates(tx: LedgerTransaction)
= tx.groupStates<State, Issued<Commodity>> { it.amount.token } = tx.groupStates<State, Issued<Commodity>> { it.amount.token }
} }
@ -137,7 +138,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
data class Exit(override val amount: Amount<Issued<Commodity>>) : Commands, FungibleAsset.Commands.Exit<Commodity> data class Exit(override val amount: Amount<Issued<Commodity>>) : Commands, FungibleAsset.Commands.Exit<Commodity>
} }
override fun verify(tx: TransactionForContract) override fun verify(tx: LedgerTransaction)
= verifyClause(tx, Clauses.Group(), extractCommands(tx.commands)) = verifyClause(tx, Clauses.Group(), extractCommands(tx.commands))
override fun extractCommands(commands: Collection<AuthenticatedObject<CommandData>>): List<AuthenticatedObject<Commands>> override fun extractCommands(commands: Collection<AuthenticatedObject<CommandData>>): List<AuthenticatedObject<Commands>>

View File

@ -15,6 +15,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.NonEmptySet
@ -74,7 +75,7 @@ class Obligation<P : Any> : Contract {
) )
) )
) { ) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<Obligation.State<P>, Issued<Terms<P>>>>
= tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.amount.token } = tx.groupStates<Obligation.State<P>, Issued<Terms<P>>> { it.amount.token }
} }
@ -97,7 +98,7 @@ class Obligation<P : Any> : Contract {
val lifecycleClause = Clauses.VerifyLifecycle<ContractState, C, Unit, P>() val lifecycleClause = Clauses.VerifyLifecycle<ContractState, C, Unit, P>()
override fun toString(): String = "Net obligations" override fun toString(): String = "Net obligations"
override fun verify(tx: TransactionForContract, inputs: List<ContractState>, outputs: List<ContractState>, commands: List<AuthenticatedObject<C>>, groupingKey: Unit?): Set<C> { override fun verify(tx: LedgerTransaction, inputs: List<ContractState>, outputs: List<ContractState>, commands: List<AuthenticatedObject<C>>, groupingKey: Unit?): Set<C> {
lifecycleClause.verify(tx, inputs, outputs, commands, groupingKey) lifecycleClause.verify(tx, inputs, outputs, commands, groupingKey)
return super.verify(tx, inputs, outputs, commands, groupingKey) return super.verify(tx, inputs, outputs, commands, groupingKey)
} }
@ -109,7 +110,7 @@ class Obligation<P : Any> : Contract {
class SetLifecycle<P : Any> : Clause<State<P>, Commands, Issued<Terms<P>>>() { class SetLifecycle<P : Any> : Clause<State<P>, Commands, Issued<Terms<P>>>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.SetLifecycle::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.SetLifecycle::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State<P>>, inputs: List<State<P>>,
outputs: List<State<P>>, outputs: List<State<P>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -128,7 +129,7 @@ class Obligation<P : Any> : Contract {
*/ */
class Settle<P : Any> : Clause<State<P>, Commands, Issued<Terms<P>>>() { class Settle<P : Any> : Clause<State<P>, Commands, Issued<Terms<P>>>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Settle::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Settle::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State<P>>, inputs: List<State<P>>,
outputs: List<State<P>>, outputs: List<State<P>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -158,7 +159,7 @@ class Obligation<P : Any> : Contract {
// Move (signed by B) // Move (signed by B)
// //
// That would pass this check. Ensuring they do not is best addressed in the transaction generation stage. // That would pass this check. Ensuring they do not is best addressed in the transaction generation stage.
val assetStates = tx.outputs.filterIsInstance<FungibleAsset<*>>() val assetStates = tx.outputs.map { it.data }.filterIsInstance<FungibleAsset<*>>()
val acceptableAssetStates = assetStates val acceptableAssetStates = assetStates
// TODO: This filter is nonsense, because it just checks there is an asset contract loaded, we need to // TODO: This filter is nonsense, because it just checks there is an asset contract loaded, we need to
// verify the asset contract is the asset contract we expect. // verify the asset contract is the asset contract we expect.
@ -216,7 +217,7 @@ class Obligation<P : Any> : Contract {
* non-standard lifecycle states on input/output. * non-standard lifecycle states on input/output.
*/ */
class VerifyLifecycle<S : ContractState, C : CommandData, T : Any, P : Any> : Clause<S, C, T>() { class VerifyLifecycle<S : ContractState, C : CommandData, T : Any, P : Any> : Clause<S, C, T>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,
@ -385,7 +386,7 @@ class Obligation<P : Any> : Contract {
data class Exit<P : Any>(override val amount: Amount<Issued<Terms<P>>>) : Commands, FungibleAsset.Commands.Exit<Terms<P>> data class Exit<P : Any>(override val amount: Amount<Issued<Terms<P>>>) : Commands, FungibleAsset.Commands.Exit<Terms<P>>
} }
override fun verify(tx: TransactionForContract) = verifyClause<Commands>(tx, FirstOf<ContractState, Commands, Unit>( override fun verify(tx: LedgerTransaction) = verifyClause<Commands>(tx, FirstOf<ContractState, Commands, Unit>(
Clauses.Net<Commands, P>(), Clauses.Net<Commands, P>(),
Clauses.Group<P>() Clauses.Group<P>()
), tx.commands.select<Obligation.Commands>()) ), tx.commands.select<Obligation.Commands>())
@ -396,7 +397,7 @@ class Obligation<P : Any> : Contract {
@VisibleForTesting @VisibleForTesting
private fun verifySetLifecycleCommand(inputs: List<FungibleAsset<Terms<P>>>, private fun verifySetLifecycleCommand(inputs: List<FungibleAsset<Terms<P>>>,
outputs: List<FungibleAsset<Terms<P>>>, outputs: List<FungibleAsset<Terms<P>>>,
tx: TransactionForContract, tx: LedgerTransaction,
setLifecycleCommand: AuthenticatedObject<Commands.SetLifecycle>) { setLifecycleCommand: AuthenticatedObject<Commands.SetLifecycle>) {
// Default must not change anything except lifecycle, so number of inputs and outputs must match // Default must not change anything except lifecycle, so number of inputs and outputs must match
// exactly. // exactly.

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.OnLedgerAsset
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import java.security.PublicKey import java.security.PublicKey
@ -37,7 +38,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
generateExitCommand: (Amount<Issued<T>>) -> CommandData): Set<PublicKey> generateExitCommand: (Amount<Issued<T>>) -> CommandData): Set<PublicKey>
= OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand) = OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -2,6 +2,7 @@ package net.corda.contracts.clause
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.transactions.LedgerTransaction
/** /**
* Standard issue clause for contracts that issue fungible assets. * Standard issue clause for contracts that issue fungible assets.
@ -17,7 +18,7 @@ abstract class AbstractIssue<in S : ContractState, C : CommandData, T : Any>(
val sum: List<S>.() -> Amount<Issued<T>>, val sum: List<S>.() -> Amount<Issued<T>>,
val sumOrZero: List<S>.(token: Issued<T>) -> Amount<Issued<T>> val sumOrZero: List<S>.(token: Issued<T>) -> Amount<Issued<T>>
) : Clause<S, C, Issued<T>>() { ) : Clause<S, C, Issued<T>>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -9,6 +9,7 @@ import net.corda.contracts.asset.sumAmountsDue
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -49,7 +50,7 @@ open class NetClause<C : CommandData, P : Any> : Clause<ContractState, C, Unit>(
override val requiredCommands: Set<Class<out CommandData>> = setOf(Obligation.Commands.Net::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Obligation.Commands.Net::class.java)
@Suppress("ConvertLambdaToReference") @Suppress("ConvertLambdaToReference")
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -2,13 +2,14 @@ package net.corda.contracts.clause
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.transactions.LedgerTransaction
/** /**
* Clause for fungible asset contracts, which enforces that no output state should have * Clause for fungible asset contracts, which enforces that no output state should have
* a balance of zero. * a balance of zero.
*/ */
open class NoZeroSizedOutputs<in S : FungibleAsset<T>, C : CommandData, T : Any> : Clause<S, C, Issued<T>>() { open class NoZeroSizedOutputs<in S : FungibleAsset<T>, C : CommandData, T : Any> : Clause<S, C, Issued<T>>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<S>, inputs: List<S>,
outputs: List<S>, outputs: List<S>,
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,

View File

@ -8,13 +8,16 @@ import net.corda.core.contracts.clauses.AllOf
import net.corda.core.contracts.clauses.FirstOf import net.corda.core.contracts.clauses.FirstOf
import net.corda.core.contracts.clauses.GroupClauseVerifier import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.* import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.newSecureRandom
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.schemas.SampleCashSchemaV1 import net.corda.schemas.SampleCashSchemaV1
@ -36,7 +39,7 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
ConserveAmount()) ConserveAmount())
) )
) { ) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, Issued<Currency>>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, Issued<Currency>>>
= tx.groupStates<State, Issued<Currency>> { it.amount.token } = tx.groupStates<State, Issued<Currency>> { it.amount.token }
} }
@ -126,7 +129,7 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
override fun generateIssueCommand() = Commands.Issue() override fun generateIssueCommand() = Commands.Issue()
override fun generateMoveCommand() = Commands.Move() override fun generateMoveCommand() = Commands.Move()
override fun verify(tx: TransactionForContract) override fun verify(tx: LedgerTransaction)
= verifyClause(tx, Clauses.Group(), extractCommands(tx.commands)) = verifyClause(tx, Clauses.Group(), extractCommands(tx.commands))
} }

View File

@ -8,9 +8,8 @@ import io.requery.rx.KotlinRxEntityStore
import io.requery.sql.* import io.requery.sql.*
import io.requery.sql.platform.Generic import io.requery.sql.platform.Generic
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.testing.contracts.DummyContract
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -26,6 +25,7 @@ import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.contracts.DummyContract
import org.h2.jdbcx.JdbcDataSource import org.h2.jdbcx.JdbcDataSource
import org.junit.After import org.junit.After
import org.junit.Assert import org.junit.Assert
@ -94,7 +94,7 @@ class VaultSchemaTest {
class Create : TypeOnlyCommandData(), Commands class Create : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Always accepts. // Always accepts.
} }
} }

View File

@ -4,10 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.InputStreamAndHash import net.corda.core.InputStreamAndHash
import net.corda.core.InputStreamAndHash.Companion.createInMemoryTestZip
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
@ -16,10 +14,11 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.Emoji
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startTrackedFlow import net.corda.core.messaging.startTrackedFlow
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.internal.Emoji
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B
@ -173,8 +172,8 @@ class AttachmentContract : Contract {
override val legalContractReference: SecureHash override val legalContractReference: SecureHash
get() = SecureHash.zeroHash // TODO not implemented get() = SecureHash.zeroHash // TODO not implemented
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
val state = tx.outputs.filterIsInstance<AttachmentContract.State>().single() val state = tx.outputs.map { it.data }.filterIsInstance<AttachmentContract.State>().single()
val attachment = tx.attachments.single() val attachment = tx.attachments.single()
require(state.hash == attachment.id) require(state.hash == attachment.id)
} }

View File

@ -1,9 +1,7 @@
package net.corda.irs.contract package net.corda.irs.contract
import net.corda.contracts.*
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty import net.corda.contracts.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -13,6 +11,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.irs.api.NodeInterestRates import net.corda.irs.api.NodeInterestRates
import net.corda.irs.flows.FixingFlow import net.corda.irs.flows.FixingFlow
@ -461,7 +460,7 @@ class InterestRateSwap : Contract {
fixingCalendar, index, indexSource, indexTenor) fixingCalendar, index, indexSource, indexTenor)
} }
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.TimeWindow(), Clauses.Group()), tx.commands.select<Commands>()) override fun verify(tx: LedgerTransaction) = verifyClause(tx, AllOf(Clauses.TimeWindow(), Clauses.Group()), tx.commands.select<Commands>())
interface Clauses { interface Clauses {
/** /**
@ -512,13 +511,13 @@ class InterestRateSwap : Contract {
class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) { class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) {
// Group by Trade ID for in / out states // Group by Trade ID for in / out states
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, UniqueIdentifier>> { override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<State, UniqueIdentifier>> {
return tx.groupStates { state -> state.linearId } return tx.groupStates { state -> state.linearId }
} }
} }
class TimeWindow : Clause<ContractState, Commands, Unit>() { class TimeWindow : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -532,7 +531,7 @@ class InterestRateSwap : Contract {
class Agree : AbstractIRSClause() { class Agree : AbstractIRSClause() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -568,7 +567,7 @@ class InterestRateSwap : Contract {
class Fix : AbstractIRSClause() { class Fix : AbstractIRSClause() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -613,7 +612,7 @@ class InterestRateSwap : Contract {
class Pay : AbstractIRSClause() { class Pay : AbstractIRSClause() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -629,7 +628,7 @@ class InterestRateSwap : Contract {
class Mature : AbstractIRSClause() { class Mature : AbstractIRSClause() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<State>, inputs: List<State>,
outputs: List<State>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,

View File

@ -3,13 +3,14 @@ package net.corda.vega.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import java.math.BigDecimal import java.math.BigDecimal
/** /**
* Specifies the contract between two parties that trade an OpenGamma IRS. Currently can only agree to trade. * Specifies the contract between two parties that trade an OpenGamma IRS. Currently can only agree to trade.
*/ */
data class OGTrade(override val legalContractReference: SecureHash = SecureHash.sha256("OGTRADE.KT")) : Contract { data class OGTrade(override val legalContractReference: SecureHash = SecureHash.sha256("OGTRADE.KT")) : Contract {
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.TimeWindowed(), Clauses.Group()), tx.commands.select<Commands>()) override fun verify(tx: LedgerTransaction) = verifyClause(tx, AllOf(Clauses.TimeWindowed(), Clauses.Group()), tx.commands.select<Commands>())
interface Commands : CommandData { interface Commands : CommandData {
class Agree : TypeOnlyCommandData(), Commands // Both sides agree to trade class Agree : TypeOnlyCommandData(), Commands // Both sides agree to trade
@ -17,7 +18,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
interface Clauses { interface Clauses {
class TimeWindowed : Clause<ContractState, Commands, Unit>() { class TimeWindowed : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -29,13 +30,13 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
} }
class Group : GroupClauseVerifier<IRSState, Commands, UniqueIdentifier>(AnyOf(Agree())) { class Group : GroupClauseVerifier<IRSState, Commands, UniqueIdentifier>(AnyOf(Agree())) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<IRSState, UniqueIdentifier>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<IRSState, UniqueIdentifier>>
// Group by Trade ID for in / out states // Group by Trade ID for in / out states
= tx.groupStates { state -> state.linearId } = tx.groupStates { state -> state.linearId }
} }
class Agree : Clause<IRSState, Commands, UniqueIdentifier>() { class Agree : Clause<IRSState, Commands, UniqueIdentifier>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<IRSState>, inputs: List<IRSState>,
outputs: List<IRSState>, outputs: List<IRSState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,

View File

@ -3,6 +3,7 @@ package net.corda.vega.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
/** /**
* Specifies the contract between two parties that are agreeing to a portfolio of trades and valuating that portfolio. * Specifies the contract between two parties that are agreeing to a portfolio of trades and valuating that portfolio.
@ -10,7 +11,7 @@ import net.corda.core.crypto.SecureHash
* of the portfolio arbitrarily. * of the portfolio arbitrarily.
*/ */
data class PortfolioSwap(override val legalContractReference: SecureHash = SecureHash.sha256("swordfish")) : Contract { data class PortfolioSwap(override val legalContractReference: SecureHash = SecureHash.sha256("swordfish")) : Contract {
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.TimeWindowed(), Clauses.Group()), tx.commands.select<Commands>()) override fun verify(tx: LedgerTransaction) = verifyClause(tx, AllOf(Clauses.TimeWindowed(), Clauses.Group()), tx.commands.select<Commands>())
interface Commands : CommandData { interface Commands : CommandData {
class Agree : TypeOnlyCommandData(), Commands // Both sides agree to portfolio class Agree : TypeOnlyCommandData(), Commands // Both sides agree to portfolio
@ -19,7 +20,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
interface Clauses { interface Clauses {
class TimeWindowed : Clause<ContractState, Commands, Unit>() { class TimeWindowed : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>, outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -31,7 +32,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
} }
class Group : GroupClauseVerifier<PortfolioState, Commands, UniqueIdentifier>(FirstOf(Agree(), Update())) { class Group : GroupClauseVerifier<PortfolioState, Commands, UniqueIdentifier>(FirstOf(Agree(), Update())) {
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<PortfolioState, UniqueIdentifier>> override fun groupStates(tx: LedgerTransaction): List<LedgerTransaction.InOutGroup<PortfolioState, UniqueIdentifier>>
// Group by Trade ID for in / out states // Group by Trade ID for in / out states
= tx.groupStates { state -> state.linearId } = tx.groupStates { state -> state.linearId }
} }
@ -39,7 +40,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
class Update : Clause<PortfolioState, Commands, UniqueIdentifier>() { class Update : Clause<PortfolioState, Commands, UniqueIdentifier>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Update::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Update::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<PortfolioState>, inputs: List<PortfolioState>,
outputs: List<PortfolioState>, outputs: List<PortfolioState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
@ -60,7 +61,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
class Agree : Clause<PortfolioState, Commands, UniqueIdentifier>() { class Agree : Clause<PortfolioState, Commands, UniqueIdentifier>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: LedgerTransaction,
inputs: List<PortfolioState>, inputs: List<PortfolioState>,
outputs: List<PortfolioState>, outputs: List<PortfolioState>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,

View File

@ -1,10 +1,10 @@
package net.corda.testing package net.corda.testing
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.TransactionForContract
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
class AlwaysSucceedContract(override val legalContractReference: SecureHash = SecureHash.sha256("Always succeed contract")) : Contract { class AlwaysSucceedContract(override val legalContractReference: SecureHash = SecureHash.sha256("Always succeed contract")) : Contract {
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
} }
} }

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
@ -39,7 +40,7 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
class Move : TypeOnlyCommandData(), Commands class Move : TypeOnlyCommandData(), Commands
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
// Always accepts. // Always accepts.
} }

View File

@ -2,9 +2,10 @@ package net.corda.testing.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.WireTransaction
import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.WireTransaction
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2() val DUMMY_V2_PROGRAM_ID = DummyContractV2()
@ -30,7 +31,7 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
return State(state.magicNumber, state.participants) return State(state.magicNumber, state.participants)
} }
override fun verify(tx: TransactionForContract) { override fun verify(tx: LedgerTransaction) {
if (tx.commands.any { it.value is UpgradeCommand }) ContractUpgradeFlow.verify(tx) if (tx.commands.any { it.value is UpgradeCommand }) ContractUpgradeFlow.verify(tx)
// Other verifications. // Other verifications.
} }

View File

@ -2,7 +2,6 @@ package net.corda.testing.contracts
import net.corda.contracts.DealState import net.corda.contracts.DealState
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny import net.corda.core.crypto.containsAny
@ -11,14 +10,15 @@ import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState import net.corda.core.schemas.QueryableState
import net.corda.testing.schemas.DummyDealStateSchemaV1 import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.schemas.DummyDealStateSchemaV1
import java.security.PublicKey import java.security.PublicKey
class DummyDealContract : Contract { class DummyDealContract : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("TestDeal") override val legalContractReference: SecureHash = SecureHash.sha256("TestDeal")
override fun verify(tx: TransactionForContract) {} override fun verify(tx: LedgerTransaction) {}
data class State( data class State(
override val contract: Contract = DummyDealContract(), override val contract: Contract = DummyDealContract(),

View File

@ -1,6 +1,9 @@
package net.corda.testing.contracts package net.corda.testing.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.FilterOn import net.corda.core.contracts.clauses.FilterOn
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
@ -10,6 +13,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.schemas.DummyLinearStateSchemaV1 import net.corda.testing.schemas.DummyLinearStateSchemaV1
import net.corda.testing.schemas.DummyLinearStateSchemaV2 import net.corda.testing.schemas.DummyLinearStateSchemaV2
import java.time.LocalDateTime import java.time.LocalDateTime
@ -19,7 +23,7 @@ class DummyLinearContract : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("Test") override val legalContractReference: SecureHash = SecureHash.sha256("Test")
val clause: Clause<State, CommandData, Unit> = LinearState.ClauseVerifier() val clause: Clause<State, CommandData, Unit> = LinearState.ClauseVerifier()
override fun verify(tx: TransactionForContract) = verifyClause(tx, override fun verify(tx: LedgerTransaction) = verifyClause(tx,
FilterOn(clause, { states -> states.filterIsInstance<State>() }), FilterOn(clause, { states -> states.filterIsInstance<State>() }),
emptyList()) emptyList())