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.identity.AbstractParty
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 java.io.FileNotFoundException
import java.io.IOException
@ -205,7 +209,7 @@ interface LinearState : ContractState {
*/
@CordaSerializable
class ClauseVerifier<in S : LinearState, C : CommandData> : Clause<S, C, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
commands: List<AuthenticatedObject<C>>,
@ -399,7 +403,7 @@ interface Contract {
* existing contract code.
*/
@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

View File

@ -122,12 +122,11 @@ sealed class TransactionType {
* If any contract fails to verify, the whole transaction is considered to be invalid.
*/
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.
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) {
try {
contract.verify(ctx)
contract.verify(tx)
} catch(e: Throwable) {
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
import java.util.*
/**
@ -24,7 +24,7 @@ open class AllOf<S : ContractState, C : CommandData, K : Any>(firstClause: Claus
return clauses
}
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
import java.util.*
/**
@ -18,7 +18,7 @@ open class AnyOf<in S : ContractState, C : CommandData, in K : Any>(vararg rawCl
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 ->
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
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],
* but may be further reduced by clauses such as [GroupClauseVerifier].
* @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
* 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
* commands that were not required (for example the Exit command for fungible assets is optional).
*/
@Throws(IllegalStateException::class)
abstract fun verify(tx: TransactionForContract,
abstract fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
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.CommandData
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.
@ -15,15 +15,15 @@ import net.corda.core.contracts.TransactionForContract
* @param commands commands extracted from the transaction, which are relevant to the
* clauses.
*/
fun <C : CommandData> verifyClause(tx: TransactionForContract,
fun <C : CommandData> verifyClause(tx: LedgerTransaction,
clause: Clause<ContractState, C, Unit>,
commands: List<AuthenticatedObject<C>>) {
if (Clause.log.isTraceEnabled) {
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) }
}

View File

@ -3,7 +3,7 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData
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.
@ -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<*, *, *>>
= clause.getExecutionPath(commands)
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
import java.util.*
/**
@ -19,7 +19,7 @@ class FirstComposition<S : ContractState, C : CommandData, K : Any>(firstClause:
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")
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.CommandData
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 java.util.*
@ -33,7 +33,7 @@ class FirstOf<S : ContractState, C : CommandData, K : Any>(firstClause: Clause<S
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)
}

View File

@ -3,16 +3,16 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
import java.util.*
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<*, *, *>>
= clause.getExecutionPath(commands)
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
commands: List<AuthenticatedObject<C>>,

View File

@ -1,6 +1,7 @@
package net.corda.core.flows
import net.corda.core.contracts.*
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
@ -21,9 +22,9 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
companion object {
@JvmStatic
fun verify(tx: TransactionForContract) {
fun verify(tx: LedgerTransaction) {
// 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

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey
import java.util.*
/**
* 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
// currently sends this across to out-of-process verifiers. We'll need to change that first.
// DOCSTART 1
@CordaSerializable
class LedgerTransaction(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
@ -35,6 +37,7 @@ class LedgerTransaction(
timeWindow: TimeWindow?,
type: TransactionType
) : BaseTransaction(inputs, outputs, notary, signers, type, timeWindow) {
//DOCEND 1
init {
checkInvariants()
}
@ -42,14 +45,6 @@ class LedgerTransaction(
@Suppress("UNCHECKED_CAST")
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:
*
@ -88,4 +83,57 @@ class LedgerTransaction(
result = 31 * result + id.hashCode()
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.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.ledger
@ -28,8 +29,8 @@ class TransactionEncumbranceTests {
class DummyTimeLock : Contract {
override val legalContractReference = SecureHash.sha256("DummyTimeLock")
override fun verify(tx: TransactionForContract) {
val timeLockInput = tx.inputs.filterIsInstance<State>().singleOrNull() ?: return
override fun verify(tx: LedgerTransaction) {
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")
requireThat {
"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.CommandData
import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals
@ -15,7 +16,7 @@ class AllOfTests {
fun minimal() {
val counter = AtomicInteger(0)
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>>())
// Check that we've run the verify() function of two clauses
@ -25,7 +26,7 @@ class AllOfTests {
@Test
fun `not all match`() {
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>>()) }
}
}

View File

@ -2,8 +2,9 @@ package net.corda.core.contracts.clauses
import net.corda.core.contracts.AuthenticatedObject
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.transactions.LedgerTransaction
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals
@ -14,7 +15,7 @@ class AnyOfTests {
fun minimal() {
val counter = AtomicInteger(0)
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>>())
// Check that we've run the verify() function of two clauses
@ -25,7 +26,7 @@ class AnyOfTests {
fun `not all match`() {
val counter = AtomicInteger(0)
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>>())
// Check that we've run the verify() function of one clause
@ -36,7 +37,7 @@ class AnyOfTests {
fun `none match`() {
val counter = AtomicInteger(0)
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) {
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.transactions.LedgerTransaction
import java.util.concurrent.atomic.AtomicInteger
internal fun matchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() {
override val requiredCommands: Set<Class<out CommandData>> = emptySet()
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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 */
internal fun unmatchedClause(counter: AtomicInteger? = null) = object : Clause<ContractState, CommandData, Unit>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(object : CommandData {}.javaClass)
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.testing.contracts.DummyContract
import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.contracts.DummyContract
import org.junit.Test
import kotlin.test.assertFailsWith
@ -17,25 +18,25 @@ class VerifyClausesTests {
@Test
fun minimal() {
val clause = object : Clause<ContractState, CommandData, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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>>())
}
@Test
fun errorSuperfluousCommands() {
val clause = object : Clause<ContractState, CommandData, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
commands: List<AuthenticatedObject<CommandData>>, groupingKey: Unit?): Set<CommandData> = emptySet()
}
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
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.startFlow
import net.corda.core.node.services.unconsumedStates
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.internal.Emoji
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 verify(tx: TransactionForContract) {}
override fun verify(tx: LedgerTransaction) {}
// Dummy Cash contract for testing.
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.node.services.AttachmentStorage
import net.corda.core.serialization.*
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MEGA_CORP
@ -58,7 +59,7 @@ class AttachmentClassLoaderTests {
class Create : TypeOnlyCommandData(), Commands
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
// Always accepts.
}

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.DUMMY_NOTARY
import org.junit.Test
import kotlin.test.assertEquals
@ -13,7 +14,7 @@ class VaultUpdateTests {
object DummyContract : Contract {
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
}
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.crypto.SecureHash
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.utilities.seconds
import net.corda.testing.*
import net.corda.testing.node.MockServices
import org.junit.Before
@ -20,7 +21,7 @@ class TransactionSerializationTests {
class TestCash : Contract {
override val legalContractReference = SecureHash.sha256("TestCash")
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
}
data class State(

View File

@ -1,6 +1,10 @@
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.flows.FlowException
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.KryoAMQPSerializer
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.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY
@ -505,7 +509,7 @@ class SerializationOutputTests {
}
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:
* ``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
* ``legalContractReference`` is the hash of the legal prose contract that ``verify`` seeks to express in code
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
considered valid.
``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.
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
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
// Always accepts!
}
.. sourcecode:: java
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
// Always accepts!
}
@ -63,39 +63,40 @@ And here is the ``verify`` that rejects all transactions:
.. sourcecode:: kotlin
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
throw IllegalArgumentException("Always rejects!")
}
.. sourcecode:: java
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
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:
.. 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
:start-after: DOCSTART 1
:end-before: DOCEND 1
Where:
* ``inputs`` is a list of the transaction's inputs
* ``outputs`` is a list of the transaction's outputs
* ``attachments`` is a list of the transaction's attachments
* ``commands`` is a list of the transaction's commands, and their associated signatures
* ``origHash`` is the transaction's hash
* ``inputNotary`` is the transaction's notary
* ``timestamp`` is the transaction's timestamp
* ``inputs`` is a list of the transaction's inputs'
* ``outputs`` is a list of the transaction's outputs'
* ``attachments`` is a list of the transaction's attachments'
* ``commands`` is a list of the transaction's commands, and their associated signatures'
* ``id`` is the transaction's merkle root hash'
* ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions.
* ``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()
^^^^^^^^^^^^^
@ -134,7 +135,7 @@ exception will cause the transaction to be rejected.
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
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
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
when (command.value) {
@ -200,7 +201,7 @@ execution of ``verify()``:
}
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
final AuthenticatedObject<Commands> command = requireSingleCommand(tx.getCommands(), Commands.class);
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
: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

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

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()
@Throws(IllegalStateException::class)
abstract fun verify(tx: TransactionForContract,
abstract fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
commands: List<AuthenticatedObject<C>>,
@ -47,7 +47,7 @@ An example ``verify`` from ``Obligation`` contract:
.. 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.Group<P>()
), tx.commands.select<Obligation.Commands>())
@ -112,7 +112,7 @@ Example from ``CommercialPaper.kt``:
Redeem(),
Move(),
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 }
}
@ -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 }
}
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.
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) }) {
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>,
outputs: List<State>,
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.services.linearHeadsOfType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.seconds
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
* 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>()
require(tx.timeWindow?.midpoint != null) { "must have a time-window" }
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 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 {
"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)

View File

@ -36,7 +36,7 @@ Just as every Corda state must implement the ``ContractState`` interface, every
interface Contract {
// Implements the contract constraints in code.
@Throws(IllegalArgumentException::class)
fun verify(tx: TransactionForContract)
fun verify(tx: LedgerTransaction)
// Expresses the contract constraints as legal prose.
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.
class Create : CommandData
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Create>()
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)
// 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 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.CommandData;
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.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 {}
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
final AuthenticatedObject<Create> command = requireSingleCommand(tx.getCommands(), Create.class);
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);
// 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 borrower = out.getBorrower();
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 {
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 {
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
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
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>>
get() = setOf(Commands.Move::class.java)
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<State>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -158,7 +158,7 @@ and is included in the ``CommercialPaper.kt`` code.
@NotNull
@Override
public Set<Commands> verify(@NotNull TransactionForContract tx,
public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@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(),
Move(),
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 }
}
@ -246,7 +246,7 @@ its subclauses (wrapped move, issue, redeem). "Any" in this case means that it w
@NotNull
@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);
}
}

View File

@ -61,7 +61,7 @@ Kotlin syntax works.
class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
TODO()
}
}
@ -75,7 +75,7 @@ Kotlin syntax works.
}
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
throw new UnsupportedOperationException();
}
}
@ -298,7 +298,7 @@ run two contracts one time each: Cash and CommercialPaper.
.. 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.
val groups = tx.groupStates(State::withoutOwner)
@ -309,7 +309,7 @@ run two contracts one time each: Cash and CommercialPaper.
.. sourcecode:: java
@Override
public void verify(TransactionForContract tx) {
public void verify(LedgerTransaction tx) {
List<InOutGroup<State, State>> groups = tx.groupStates(State.class, State::withoutOwner);
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
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
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.
@ -791,7 +791,7 @@ The time-lock contract mentioned above can be implemented very simply:
class TestTimeLock : Contract {
...
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
val time = tx.timestamp.before ?: throw IllegalStateException(...)
...
requireThat {

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
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 java.math.BigDecimal
import java.time.Instant
@ -37,14 +38,14 @@ class UniversalContract : Contract {
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 StartDate -> null
is EndDate -> null
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 PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right)
is Const<Boolean> -> expr.value
@ -57,7 +58,7 @@ class UniversalContract : Contract {
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) {
is Const<BigDecimal> -> expr.value
is UnaryPlus -> {
@ -94,7 +95,7 @@ class UniversalContract : Contract {
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 -> {
val amount = eval(tx, arrangement.amount)
requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) }
@ -179,7 +180,7 @@ class UniversalContract : Contract {
else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name)
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
requireThat {
"transaction has a single command".using(tx.commands.size == 1)
@ -191,7 +192,7 @@ class UniversalContract : Contract {
when (value) {
is Commands.Action -> {
val inState = tx.inputs.single() as State
val inState = tx.inputs.single().state.data as State
val arr = when (inState.details) {
is Actions -> inState.details
is RollOut -> reduceRollOut(inState.details)
@ -221,7 +222,7 @@ class UniversalContract : Contract {
when (tx.outputs.size) {
1 -> {
val outState = tx.outputs.single() as State
val outState = tx.outputs.single().data as State
requireThat {
"output state must match action result state" using (arrangement.equals(outState.details))
"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")
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 {
"output states must match action result state" using (arrangement.equals(allContracts))
@ -239,15 +240,15 @@ class UniversalContract : Contract {
}
}
is Commands.Issue -> {
val outState = tx.outputs.single() as State
val outState = tx.outputs.single().data as State
requireThat {
"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()
}
}
is Commands.Move -> {
val inState = tx.inputs.single() as State
val outState = tx.outputs.single() as State
val inState = tx.inputs.single().state.data as State
val outState = tx.outputs.single().data as State
requireThat {
"the transaction is signed by all liable parties" using
(liableParties(outState.details).all { it in cmd.signers })
@ -256,13 +257,13 @@ class UniversalContract : Contract {
}
}
is Commands.Fix -> {
val inState = tx.inputs.single() as State
val inState = tx.inputs.single().state.data as State
val arr = when (inState.details) {
is Actions -> inState.details
is RollOut -> reduceRollOut(inState.details)
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 expectedArr = replaceFixing(tx, arr,
@ -279,7 +280,7 @@ class UniversalContract : Contract {
}
@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> =
when (perceivable) {
is Const -> perceivable
@ -299,11 +300,11 @@ class UniversalContract : Contract {
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>) =
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 =
when (arr) {
is Zero -> arr

View File

@ -1,11 +1,11 @@
package net.corda.contracts.isolated
import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash
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 java.security.PublicKey
// 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
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
// Always accepts.
}

View File

@ -7,7 +7,6 @@ import kotlin.Pair;
import kotlin.Unit;
import net.corda.contracts.asset.CashKt;
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.Clause;
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.Party;
import net.corda.core.node.services.VaultService;
import net.corda.core.transactions.LedgerTransaction;
import net.corda.core.transactions.TransactionBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -153,7 +153,7 @@ public class JavaCommercialPaper implements Contract {
@NotNull
@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);
}
}
@ -168,11 +168,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull
@Override
public Set<Commands> verify(@NotNull TransactionForContract tx,
public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
// There should be only a single input due to aggregation above
State input = Iterables.getOnlyElement(inputs);
@ -200,11 +200,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull
@Override
public Set<Commands> verify(@NotNull TransactionForContract tx,
public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
// There should be only a single input due to aggregation above
@ -217,7 +217,7 @@ public class JavaCommercialPaper implements Contract {
Instant time = null == timeWindow
? null
: 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 -> {
require.using("must be timestamped", timeWindow != null);
@ -243,11 +243,11 @@ public class JavaCommercialPaper implements Contract {
@NotNull
@Override
public Set<Commands> verify(@NotNull TransactionForContract tx,
public Set<Commands> verify(@NotNull LedgerTransaction tx,
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
State output = Iterables.getOnlyElement(outputs);
TimeWindow timeWindowCommand = tx.getTimeWindow();
@ -293,7 +293,7 @@ public class JavaCommercialPaper implements Contract {
}
@NotNull
private List<AuthenticatedObject<Commands>> extractCommands(@NotNull TransactionForContract tx) {
private List<AuthenticatedObject<Commands>> extractCommands(@NotNull LedgerTransaction tx) {
return tx.getCommands()
.stream()
.filter((AuthenticatedObject<CommandData> command) -> command.getValue() instanceof Commands)
@ -302,7 +302,7 @@ public class JavaCommercialPaper implements Contract {
}
@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));
}

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.verifyClause
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService
import net.corda.core.crypto.random63BitValue
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji
import net.corda.schemas.CommercialPaperSchemaV1
@ -57,7 +58,7 @@ class CommercialPaper : Contract {
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(
val issuance: PartyAndReference,
@ -112,7 +113,7 @@ class CommercialPaper : Contract {
Redeem(),
Move(),
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 }
}
@ -121,7 +122,7 @@ class CommercialPaper : Contract {
{ token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -140,7 +141,7 @@ class CommercialPaper : Contract {
class Move : Clause<State, Commands, Issued<Terms>>() {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -160,7 +161,7 @@ class CommercialPaper : Contract {
class Redeem : Clause<State, Commands, Issued<Terms>>() {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -171,7 +172,7 @@ class CommercialPaper : Contract {
val timeWindow = tx.timeWindow
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")
requireThat {
"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.Party
import net.corda.core.node.services.VaultService
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji
import java.time.Instant
@ -54,7 +55,7 @@ class CommercialPaperLegacy : Contract {
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.
val groups = tx.groupStates(State::withoutOwner)
@ -80,7 +81,7 @@ class CommercialPaperLegacy : Contract {
is Commands.Redeem -> {
// Redemption of the paper requires movement of on-ledger cash.
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")
requireThat {
"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.QueryableState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji
import net.corda.schemas.CashSchemaV1
@ -72,7 +73,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
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 }
}
@ -173,7 +174,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override fun generateIssueCommand() = Commands.Issue()
override fun generateMoveCommand() = Commands.Move()
override fun verify(tx: TransactionForContract)
override fun verify(tx: LedgerTransaction)
= 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.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import java.util.*
@ -69,7 +70,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/**
* 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 }
}
@ -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>
}
override fun verify(tx: TransactionForContract)
override fun verify(tx: LedgerTransaction)
= verifyClause(tx, Clauses.Group(), extractCommands(tx.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.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji
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 }
}
@ -97,7 +98,7 @@ class Obligation<P : Any> : Contract {
val lifecycleClause = Clauses.VerifyLifecycle<ContractState, C, Unit, P>()
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)
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>>>() {
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>>,
outputs: List<State<P>>,
commands: List<AuthenticatedObject<Commands>>,
@ -128,7 +129,7 @@ class Obligation<P : Any> : Contract {
*/
class Settle<P : Any> : Clause<State<P>, Commands, Issued<Terms<P>>>() {
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>>,
outputs: List<State<P>>,
commands: List<AuthenticatedObject<Commands>>,
@ -158,7 +159,7 @@ class Obligation<P : Any> : Contract {
// Move (signed by B)
//
// 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
// 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.
@ -216,7 +217,7 @@ class Obligation<P : Any> : Contract {
* non-standard lifecycle states on input/output.
*/
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>,
outputs: List<S>,
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>>
}
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.Group<P>()
), tx.commands.select<Obligation.Commands>())
@ -396,7 +397,7 @@ class Obligation<P : Any> : Contract {
@VisibleForTesting
private fun verifySetLifecycleCommand(inputs: List<FungibleAsset<Terms<P>>>,
outputs: List<FungibleAsset<Terms<P>>>,
tx: TransactionForContract,
tx: LedgerTransaction,
setLifecycleCommand: AuthenticatedObject<Commands.SetLifecycle>) {
// Default must not change anything except lifecycle, so number of inputs and outputs must match
// exactly.

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.OnLedgerAsset
import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
import java.security.PublicKey
@ -37,7 +38,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
generateExitCommand: (Amount<Issued<T>>) -> CommandData): Set<PublicKey>
= OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
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.clauses.Clause
import net.corda.core.transactions.LedgerTransaction
/**
* 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 sumOrZero: List<S>.(token: Issued<T>) -> Amount<Issued<T>>
) : Clause<S, C, Issued<T>>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<S>,
outputs: List<S>,
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.clauses.Clause
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction
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)
@Suppress("ConvertLambdaToReference")
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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.clauses.Clause
import net.corda.core.transactions.LedgerTransaction
/**
* Clause for fungible asset contracts, which enforces that no output state should have
* a balance of zero.
*/
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>,
outputs: List<S>,
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.GroupClauseVerifier
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.Party
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.internal.Emoji
import net.corda.schemas.SampleCashSchemaV1
@ -36,7 +39,7 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
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 }
}
@ -126,7 +129,7 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
override fun generateIssueCommand() = Commands.Issue()
override fun generateMoveCommand() = Commands.Move()
override fun verify(tx: TransactionForContract)
override fun verify(tx: LedgerTransaction)
= 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.platform.Generic
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.composite.CompositeKey
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty
@ -26,6 +25,7 @@ import net.corda.testing.ALICE
import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.DUMMY_NOTARY_KEY
import net.corda.testing.contracts.DummyContract
import org.h2.jdbcx.JdbcDataSource
import org.junit.After
import org.junit.Assert
@ -94,7 +94,7 @@ class VaultSchemaTest {
class Create : TypeOnlyCommandData(), Commands
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
// Always accepts.
}
}

View File

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

View File

@ -1,9 +1,7 @@
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.JsonProperty
import net.corda.contracts.*
import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.*
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.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.irs.api.NodeInterestRates
import net.corda.irs.flows.FixingFlow
@ -461,7 +460,7 @@ class InterestRateSwap : Contract {
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 {
/**
@ -512,13 +511,13 @@ class InterestRateSwap : Contract {
class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) {
// 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 }
}
}
class TimeWindow : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>,
@ -532,7 +531,7 @@ class InterestRateSwap : Contract {
class Agree : AbstractIRSClause() {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -568,7 +567,7 @@ class InterestRateSwap : Contract {
class Fix : AbstractIRSClause() {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -613,7 +612,7 @@ class InterestRateSwap : Contract {
class Pay : AbstractIRSClause() {
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>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
@ -629,7 +628,7 @@ class InterestRateSwap : Contract {
class Mature : AbstractIRSClause() {
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>,
outputs: List<State>,
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.clauses.*
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import java.math.BigDecimal
/**
* 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 {
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 {
class Agree : TypeOnlyCommandData(), Commands // Both sides agree to trade
@ -17,7 +18,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
interface Clauses {
class TimeWindowed : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
commands: List<AuthenticatedObject<Commands>>,
@ -29,13 +30,13 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
}
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
= tx.groupStates { state -> state.linearId }
}
class Agree : Clause<IRSState, Commands, UniqueIdentifier>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<IRSState>,
outputs: List<IRSState>,
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.clauses.*
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.
@ -10,7 +11,7 @@ import net.corda.core.crypto.SecureHash
* of the portfolio arbitrarily.
*/
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 {
class Agree : TypeOnlyCommandData(), Commands // Both sides agree to portfolio
@ -19,7 +20,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
interface Clauses {
class TimeWindowed : Clause<ContractState, Commands, Unit>() {
override fun verify(tx: TransactionForContract,
override fun verify(tx: LedgerTransaction,
inputs: List<ContractState>,
outputs: List<ContractState>,
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())) {
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
= tx.groupStates { state -> state.linearId }
}
@ -39,7 +40,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
class Update : Clause<PortfolioState, Commands, UniqueIdentifier>() {
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>,
outputs: List<PortfolioState>,
commands: List<AuthenticatedObject<Commands>>,
@ -60,7 +61,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
class Agree : Clause<PortfolioState, Commands, UniqueIdentifier>() {
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>,
outputs: List<PortfolioState>,
commands: List<AuthenticatedObject<Commands>>,

View File

@ -1,10 +1,10 @@
package net.corda.testing
import net.corda.core.contracts.Contract
import net.corda.core.contracts.TransactionForContract
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
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.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
// 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
}
override fun verify(tx: TransactionForContract) {
override fun verify(tx: LedgerTransaction) {
// Always accepts.
}

View File

@ -2,9 +2,10 @@ package net.corda.testing.contracts
import net.corda.core.contracts.*
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.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.
val DUMMY_V2_PROGRAM_ID = DummyContractV2()
@ -30,7 +31,7 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
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)
// Other verifications.
}

View File

@ -2,7 +2,6 @@ package net.corda.testing.contracts
import net.corda.contracts.DealState
import net.corda.core.contracts.Contract
import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.SecureHash
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.PersistentState
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.testing.schemas.DummyDealStateSchemaV1
import java.security.PublicKey
class DummyDealContract : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("TestDeal")
override fun verify(tx: TransactionForContract) {}
override fun verify(tx: LedgerTransaction) {}
data class State(
override val contract: Contract = DummyDealContract(),

View File

@ -1,6 +1,9 @@
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.FilterOn
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.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.schemas.DummyLinearStateSchemaV1
import net.corda.testing.schemas.DummyLinearStateSchemaV2
import java.time.LocalDateTime
@ -19,7 +23,7 @@ class DummyLinearContract : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("Test")
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>() }),
emptyList())