mirror of
https://github.com/corda/corda.git
synced 2024-12-19 13:08:04 +00:00
Merge pull request #1082 from corda/mnesbit-remove-transactionforcontract
Remove TransactionForContract and just use LedgerTransaction
This commit is contained in:
commit
2a70be66e5
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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>>,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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>>,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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>>()) }
|
||||
}
|
||||
}
|
||||
|
@ -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>>())
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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)) }
|
||||
}
|
||||
|
@ -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("")
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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("")
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
------------
|
||||
|
||||
|
@ -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>>,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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>>
|
||||
|
@ -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.
|
||||
|
@ -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>>,
|
||||
|
@ -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>>,
|
||||
|
@ -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>>,
|
||||
|
@ -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>>,
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>>,
|
||||
|
@ -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>>,
|
||||
|
@ -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>>,
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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())
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user