mirror of
https://github.com/corda/corda.git
synced 2025-06-11 03:41:41 +00:00
Merge pull request #1091 from corda/mnesbit-friendlier-transaction-access
Create a friendlier set of access methods to states on transactions
This commit is contained in:
commit
08d2e351d2
@ -123,7 +123,7 @@ sealed class TransactionType {
|
|||||||
*/
|
*/
|
||||||
private fun verifyContracts(tx: LedgerTransaction) {
|
private fun verifyContracts(tx: LedgerTransaction) {
|
||||||
// TODO: This will all be replaced in future once the sandbox and contract constraints work is done.
|
// TODO: This will all be replaced in future once the sandbox and contract constraints work is done.
|
||||||
val contracts = (tx.inputs.map { it.state.data.contract } + tx.outputs.map { it.data.contract }).toSet()
|
val contracts = (tx.inputStates.map { it.contract } + tx.outputStates.map { it.contract }).toSet()
|
||||||
for (contract in contracts) {
|
for (contract in contracts) {
|
||||||
try {
|
try {
|
||||||
contract.verify(tx)
|
contract.verify(tx)
|
||||||
@ -171,6 +171,6 @@ sealed class TransactionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
|
override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputStates.flatMap { it.participants }.map { it.owningKey }.toSet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ fun <C : CommandData> verifyClause(tx: LedgerTransaction,
|
|||||||
Clause.log.trace("Tx ${tx.id} clause: $clause")
|
Clause.log.trace("Tx ${tx.id} clause: $clause")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val matchedCommands = clause.verify(tx, tx.inputs.map { it.state.data }, tx.outputs.map { it.data }, commands, null)
|
val matchedCommands = clause.verify(tx, tx.inputStates, tx.outputStates, commands, null)
|
||||||
|
|
||||||
check(matchedCommands.containsAll(commands.map { it.value })) { "The following commands were not matched at the end of execution: " + (commands - matchedCommands) }
|
check(matchedCommands.containsAll(commands.map { it.value })) { "The following commands were not matched at the end of execution: " + (commands - matchedCommands) }
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun verify(tx: LedgerTransaction) {
|
fun verify(tx: LedgerTransaction) {
|
||||||
// Contract Upgrade transaction should have 1 input, 1 output and 1 command.
|
// Contract Upgrade transaction should have 1 input, 1 output and 1 command.
|
||||||
verify(tx.inputs.single().state.data, tx.outputs.single().data, tx.commands.map { Command(it.value, it.signers) }.single())
|
verify(tx.inputStates.single(), tx.outputStates.single(), tx.commands.map { Command(it.value, it.signers) }.single())
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -133,7 +133,7 @@ open class FinalityFlow(val transactions: Iterable<SignedTransaction>,
|
|||||||
* overriding functions.
|
* overriding functions.
|
||||||
*/
|
*/
|
||||||
protected fun extractParticipants(ltx: LedgerTransaction): List<AbstractParty> {
|
protected fun extractParticipants(ltx: LedgerTransaction): List<AbstractParty> {
|
||||||
return ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
|
return ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,8 +2,10 @@ package net.corda.core.transactions
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.indexOfOrThrow
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class defining fields shared by all transaction types in the system.
|
* An abstract class defining fields shared by all transaction types in the system.
|
||||||
@ -56,4 +58,123 @@ abstract class BaseTransaction(
|
|||||||
override fun hashCode() = Objects.hash(notary, mustSign, type, timeWindow)
|
override fun hashCode() = Objects.hash(notary, mustSign, type, timeWindow)
|
||||||
|
|
||||||
override fun toString(): String = "${javaClass.simpleName}(id=$id)"
|
override fun toString(): String = "${javaClass.simpleName}(id=$id)"
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Returns a [StateAndRef] for the given output index.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T : ContractState> outRef(index: Int): StateAndRef<T> = StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [StateAndRef] for the requested output state, or throws [IllegalArgumentException] if not found.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> outRef(state: ContractState): StateAndRef<T> = outRef(outputStates.indexOfOrThrow(state))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper property to return a list of [ContractState] objects, rather than the often less convenient [TransactionState]
|
||||||
|
*/
|
||||||
|
val outputStates: List<ContractState> get() = outputs.map { it.data }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting an indexed output.
|
||||||
|
* @param index the position of the item in the output.
|
||||||
|
* @return The ContractState at the requested index
|
||||||
|
*/
|
||||||
|
fun getOutput(index: Int): ContractState = outputs[index].data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting all output states of a particular class, interface, or base class.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of output states matching the clazz restriction.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> outputsOfType(clazz: Class<T>): List<T> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return outputs.filter { clazz.isInstance(it.data) }.map { it.data as T }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify filtering outputs according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of output states matching the predicate and clazz restrictions.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> filterOutputs(predicate: Predicate<T>, clazz: Class<T>): List<T> {
|
||||||
|
return outputsOfType(clazz).filter { predicate.test(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify finding a single output matching a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if this is the desired item.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the single item matching the predicate.
|
||||||
|
* @throws IllegalArgumentException if no item, or multiple items are found matching the requirements.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> findOutput(predicate: Predicate<T>, clazz: Class<T>): T {
|
||||||
|
return filterOutputs(predicate, clazz).single()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting all output [StateAndRef] items of a particular state class, interface, or base class.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of output [StateAndRef<T>] states matching the clazz restriction.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> outRefsOfType(clazz: Class<T>): List<StateAndRef<T>> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return outputs.mapIndexed { index, state -> StateAndRef(state, StateRef(id, index)) }
|
||||||
|
.filter { clazz.isInstance(it.state.data) }
|
||||||
|
.map { it as StateAndRef<T> }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify filtering output [StateAndRef] items according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of output [StateAndRef] states matching the predicate and clazz restrictions.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> filterOutRefs(predicate: Predicate<T>, clazz: Class<T>): List<StateAndRef<T>> {
|
||||||
|
return outRefsOfType(clazz).filter { predicate.test(it.state.data) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify finding a single output [StateAndRef] matching a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if this is the desired item.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* Clazz must be an extension of [ContractState].
|
||||||
|
* @return the single [StateAndRef] item matching the predicate.
|
||||||
|
* @throws IllegalArgumentException if no item, or multiple items are found matching the requirements.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> findOutRef(predicate: Predicate<T>, clazz: Class<T>): StateAndRef<T> {
|
||||||
|
return filterOutRefs(predicate, clazz).single()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Kotlin extension methods to take advantage of Kotlin's smart type inference when querying the LedgerTransaction
|
||||||
|
inline fun <reified T : ContractState> outputsOfType(): List<T> = this.outputsOfType(T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> filterOutputs(crossinline predicate: (T) -> Boolean): List<T> {
|
||||||
|
return filterOutputs(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> findOutput(crossinline predicate: (T) -> Boolean): T {
|
||||||
|
return findOutput(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> outRefsOfType(): List<StateAndRef<T>> = this.outRefsOfType(T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> filterOutRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> {
|
||||||
|
return filterOutRefs(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> findOutRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> {
|
||||||
|
return findOutRef(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
|
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
|
||||||
@ -42,8 +43,15 @@ class LedgerTransaction(
|
|||||||
checkInvariants()
|
checkInvariants()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the typed input StateAndRef at the specified index
|
||||||
|
* @param index The index into the inputs.
|
||||||
|
* @return The [StateAndRef]
|
||||||
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T : ContractState> outRef(index: Int) = StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index))
|
fun <T : ContractState> inRef(index: Int) = inputs[index] as StateAndRef<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies this transaction and throws an exception if not valid, depending on the type. For general transactions:
|
* Verifies this transaction and throws an exception if not valid, depending on the type. For general transactions:
|
||||||
@ -100,8 +108,8 @@ class LedgerTransaction(
|
|||||||
*/
|
*/
|
||||||
// DOCSTART 2
|
// DOCSTART 2
|
||||||
fun <T : ContractState, K : Any> groupStates(ofType: Class<T>, selector: (T) -> K): List<InOutGroup<T, K>> {
|
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 inputs = inputsOfType(ofType)
|
||||||
val outputs = outputs.map { it.data }.filterIsInstance(ofType)
|
val outputs = outputsOfType(ofType)
|
||||||
|
|
||||||
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
|
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
|
||||||
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
|
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
|
||||||
@ -136,4 +144,166 @@ class LedgerTransaction(
|
|||||||
// DOCSTART 3
|
// DOCSTART 3
|
||||||
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
|
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
|
||||||
// DOCEND 3
|
// DOCEND 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting an indexed input [ContractState].
|
||||||
|
* @param index the position of the item in the inputs.
|
||||||
|
* @return The [StateAndRef] at the requested index
|
||||||
|
*/
|
||||||
|
fun getInput(index: Int): ContractState = inputs[index].state.data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting all inputs states of a particular class, interface, or base class.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of inputs matching the clazz restriction.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> inputsOfType(clazz: Class<T>): List<T> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return inputs.map { it.state.data }.filterIsInstance(clazz)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting all inputs states of a particular class, interface, or base class.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of inputs [StateAndRef] matching the clazz restriction.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> inRefsOfType(clazz: Class<T>): List<StateAndRef<T>> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return inputs.filter { clazz.isInstance(it.state.data) }.map { it as StateAndRef<T> }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify filtering inputs according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of input states matching the predicate and clazz restrictions.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> filterInputs(predicate: Predicate<T>, clazz: Class<T>): List<T> = inputsOfType(clazz).filter { predicate.test(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify filtering inputs according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [ContractState].
|
||||||
|
* @return the possibly empty list of inputs [StateAndRef] matching the predicate and clazz restrictions.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> filterInRefs(predicate: Predicate<T>, clazz: Class<T>): List<StateAndRef<T>> = inRefsOfType(clazz).filter { predicate.test(it.state.data) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify finding a single input [ContractState] matching a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if this is the desired item.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of ContractState.
|
||||||
|
* @return the single item matching the predicate.
|
||||||
|
* @throws IllegalArgumentException if no item, or multiple items are found matching the requirements.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> findInput(predicate: Predicate<T>, clazz: Class<T>): T = filterInputs(predicate, clazz).single()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify finding a single input matching a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a state of type T and returning true if this is the desired item.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of ContractState.
|
||||||
|
* @return the single item matching the predicate.
|
||||||
|
* @throws IllegalArgumentException if no item, or multiple items are found matching the requirements.
|
||||||
|
*/
|
||||||
|
fun <T : ContractState> findInRef(predicate: Predicate<T>, clazz: Class<T>): StateAndRef<T> = filterInRefs(predicate, clazz).single()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting an indexed command.
|
||||||
|
* @param index the position of the item in the commands.
|
||||||
|
* @return The Command at the requested index
|
||||||
|
*/
|
||||||
|
fun getCommand(index: Int): Command = Command(commands[index].value, commands[index].signers)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting all [Command] items with a [CommandData] of a particular class, interface, or base class.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [CommandData].
|
||||||
|
* @return the possibly empty list of commands with [CommandData] values matching the clazz restriction.
|
||||||
|
*/
|
||||||
|
fun <T : CommandData> commandsOfType(clazz: Class<T>): List<Command> {
|
||||||
|
return commands.filter { clazz.isInstance(it.value) }.map { Command(it.value, it.signers) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify filtering [Command] items according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a [CommandData] item of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [CommandData].
|
||||||
|
* @return the possibly empty list of [Command] items with [CommandData] values matching the predicate and clazz restrictions.
|
||||||
|
*/
|
||||||
|
fun <T : CommandData> filterCommands(predicate: Predicate<T>, clazz: Class<T>): List<Command> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return commandsOfType(clazz).filter { predicate.test(it.value as T) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify finding a single [Command] items according to a [Predicate].
|
||||||
|
* @param predicate A filtering function taking a [CommandData] item of type T and returning true if it should be included in the list.
|
||||||
|
* The class filtering is applied before the predicate.
|
||||||
|
* @param clazz The class type used for filtering via an [Class.isInstance] check.
|
||||||
|
* [clazz] must be an extension of [CommandData].
|
||||||
|
* @return the [Command] item with [CommandData] values matching the predicate and clazz restrictions.
|
||||||
|
* @throws IllegalArgumentException if no items, or multiple items matched the requirements.
|
||||||
|
*/
|
||||||
|
fun <T : CommandData> findCommand(predicate: Predicate<T>, clazz: Class<T>): Command {
|
||||||
|
return filterCommands(predicate, clazz).single()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting an indexed attachment.
|
||||||
|
* @param index the position of the item in the attachments.
|
||||||
|
* @return The Attachment at the requested index.
|
||||||
|
*/
|
||||||
|
fun getAttachment(index: Int): Attachment = attachments[index]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to simplify getting an indexed attachment.
|
||||||
|
* @param id the SecureHash of the desired attachment.
|
||||||
|
* @return The Attachment with the matching id.
|
||||||
|
* @throws IllegalArgumentException if no item matches the id.
|
||||||
|
*/
|
||||||
|
fun getAttachment(id: SecureHash): Attachment = attachments.single { it.id == id }
|
||||||
|
|
||||||
|
//Kotlin extension methods to take advantage of Kotlin's smart type inference when querying the LedgerTransaction
|
||||||
|
inline fun <reified T : ContractState> inputsOfType(): List<T> = this.inputsOfType(T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> inRefsOfType(): List<StateAndRef<T>> = this.inRefsOfType(T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> filterInputs(crossinline predicate: (T) -> Boolean): List<T> {
|
||||||
|
return filterInputs(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> filterInRefs(crossinline predicate: (T) -> Boolean): List<StateAndRef<T>> {
|
||||||
|
return filterInRefs(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> findInRef(crossinline predicate: (T) -> Boolean): StateAndRef<T> {
|
||||||
|
return findInRef(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : ContractState> findInput(crossinline predicate: (T) -> Boolean): T {
|
||||||
|
return findInput(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : CommandData> commandsOfType(): List<Command> = this.commandsOfType(T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : CommandData> filterCommands(crossinline predicate: (T) -> Boolean): List<Command> {
|
||||||
|
return filterCommands(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : CommandData> findCommand(crossinline predicate: (T) -> Boolean): Command {
|
||||||
|
return findCommand(Predicate { predicate(it) }, T::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,11 @@ import net.corda.core.crypto.MerkleTree
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.indexOfOrThrow
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
|
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
|
||||||
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
|
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
|
||||||
import net.corda.core.internal.Emoji
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
@ -53,16 +52,6 @@ class WireTransaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a [StateAndRef] for the given output index. */
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
fun <T : ContractState> outRef(index: Int): StateAndRef<T> {
|
|
||||||
require(index >= 0 && index < outputs.size)
|
|
||||||
return StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a [StateAndRef] for the requested output state, or throws [IllegalArgumentException] if not found. */
|
|
||||||
fun <T : ContractState> outRef(state: ContractState): StateAndRef<T> = outRef(outputs.map { it.data }.indexOfOrThrow(state))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up identities and attachments from storage to generate a [LedgerTransaction]. A transaction is expected to
|
* Looks up identities and attachments from storage to generate a [LedgerTransaction]. A transaction is expected to
|
||||||
* have been fully resolved using the resolution flow by this point.
|
* have been fully resolved using the resolution flow by this point.
|
||||||
|
@ -0,0 +1,289 @@
|
|||||||
|
package net.corda.core.contracts
|
||||||
|
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.TestDependencyInjectionBase
|
||||||
|
import net.corda.testing.contracts.DummyContract
|
||||||
|
import net.corda.testing.node.MockServices
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.function.Predicate
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class LedgerTransactionQueryTests : TestDependencyInjectionBase() {
|
||||||
|
|
||||||
|
private lateinit var services: ServiceHub
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
services = MockServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Commands {
|
||||||
|
data class Cmd1(val id: Int) : CommandData, Commands
|
||||||
|
data class Cmd2(val id: Int) : CommandData, Commands
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class StringTypeDummyState(val data: String) : ContractState {
|
||||||
|
override val contract: Contract = DummyContract()
|
||||||
|
override val participants: List<AbstractParty> = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IntTypeDummyState(val data: Int) : ContractState {
|
||||||
|
override val contract: Contract = DummyContract()
|
||||||
|
override val participants: List<AbstractParty> = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeDummyState(data: Any): ContractState {
|
||||||
|
return when (data) {
|
||||||
|
is String -> StringTypeDummyState(data)
|
||||||
|
is Int -> IntTypeDummyState(data)
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeDummyStateAndRef(data: Any): StateAndRef<*> {
|
||||||
|
val dummyState = makeDummyState(data)
|
||||||
|
val fakeIssueTx = services.signInitialTransaction(TransactionBuilder(notary = DUMMY_NOTARY).addOutputState(dummyState))
|
||||||
|
services.recordTransactions(fakeIssueTx)
|
||||||
|
val dummyStateRef = StateRef(fakeIssueTx.id, 0)
|
||||||
|
return StateAndRef(TransactionState(dummyState, DUMMY_NOTARY, null), dummyStateRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeDummyTransaction(): LedgerTransaction {
|
||||||
|
val tx = TransactionBuilder(notary = DUMMY_NOTARY)
|
||||||
|
for (i in 0..4) {
|
||||||
|
tx.addInputState(makeDummyStateAndRef(i))
|
||||||
|
tx.addInputState(makeDummyStateAndRef(i.toString()))
|
||||||
|
tx.addOutputState(makeDummyState(i))
|
||||||
|
tx.addOutputState(makeDummyState(i.toString()))
|
||||||
|
tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey))
|
||||||
|
tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey))
|
||||||
|
}
|
||||||
|
return tx.toLedgerTransaction(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple InRef Indexer tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
assertEquals(0, ltx.inRef<IntTypeDummyState>(0).state.data.data)
|
||||||
|
assertEquals("0", ltx.inRef<StringTypeDummyState>(1).state.data.data)
|
||||||
|
assertEquals(3, ltx.inRef<IntTypeDummyState>(6).state.data.data)
|
||||||
|
assertEquals("3", ltx.inRef<StringTypeDummyState>(7).state.data.data)
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { ltx.inRef<IntTypeDummyState>(10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple OutRef Indexer tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
assertEquals(0, ltx.outRef<IntTypeDummyState>(0).state.data.data)
|
||||||
|
assertEquals("0", ltx.outRef<StringTypeDummyState>(1).state.data.data)
|
||||||
|
assertEquals(3, ltx.outRef<IntTypeDummyState>(6).state.data.data)
|
||||||
|
assertEquals("3", ltx.outRef<StringTypeDummyState>(7).state.data.data)
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { ltx.outRef<IntTypeDummyState>(10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Input Indexer tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
assertEquals(0, (ltx.getInput(0) as IntTypeDummyState).data)
|
||||||
|
assertEquals("0", (ltx.getInput(1) as StringTypeDummyState).data)
|
||||||
|
assertEquals(3, (ltx.getInput(6) as IntTypeDummyState).data)
|
||||||
|
assertEquals("3", (ltx.getInput(7) as StringTypeDummyState).data)
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { ltx.getInput(10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Output Indexer tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
assertEquals(0, (ltx.getOutput(0) as IntTypeDummyState).data)
|
||||||
|
assertEquals("0", (ltx.getOutput(1) as StringTypeDummyState).data)
|
||||||
|
assertEquals(3, (ltx.getOutput(6) as IntTypeDummyState).data)
|
||||||
|
assertEquals("3", (ltx.getOutput(7) as StringTypeDummyState).data)
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { ltx.getOutput(10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Command Indexer tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
assertEquals(0, (ltx.getCommand(0).value as Commands.Cmd1).id)
|
||||||
|
assertEquals(0, (ltx.getCommand(1).value as Commands.Cmd2).id)
|
||||||
|
assertEquals(3, (ltx.getCommand(6).value as Commands.Cmd1).id)
|
||||||
|
assertEquals(3, (ltx.getCommand(7).value as Commands.Cmd2).id)
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { ltx.getOutput(10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Inputs of type tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.inputsOfType(IntTypeDummyState::class.java)
|
||||||
|
assertEquals(5, intStates.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intStates.map { it.data })
|
||||||
|
val stringStates = ltx.inputsOfType<StringTypeDummyState>()
|
||||||
|
assertEquals(5, stringStates.size)
|
||||||
|
assertEquals(listOf("0", "1", "2", "3", "4"), stringStates.map { it.data })
|
||||||
|
val notPresentQuery = ltx.inputsOfType(FungibleAsset::class.java)
|
||||||
|
assertEquals(emptyList(), notPresentQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple InputsRefs of type tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.inRefsOfType(IntTypeDummyState::class.java)
|
||||||
|
assertEquals(5, intStates.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(ltx.inputs[0], ltx.inputs[2], ltx.inputs[4], ltx.inputs[6], ltx.inputs[8]), intStates)
|
||||||
|
val stringStates = ltx.inRefsOfType<StringTypeDummyState>()
|
||||||
|
assertEquals(5, stringStates.size)
|
||||||
|
assertEquals(listOf("0", "1", "2", "3", "4"), stringStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(ltx.inputs[1], ltx.inputs[3], ltx.inputs[5], ltx.inputs[7], ltx.inputs[9]), stringStates)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Outputs of type tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.outputsOfType(IntTypeDummyState::class.java)
|
||||||
|
assertEquals(5, intStates.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intStates.map { it.data })
|
||||||
|
val stringStates = ltx.outputsOfType<StringTypeDummyState>()
|
||||||
|
assertEquals(5, stringStates.size)
|
||||||
|
assertEquals(listOf("0", "1", "2", "3", "4"), stringStates.map { it.data })
|
||||||
|
val notPresentQuery = ltx.outputsOfType(FungibleAsset::class.java)
|
||||||
|
assertEquals(emptyList(), notPresentQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple OutputsRefs of type tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.outRefsOfType(IntTypeDummyState::class.java)
|
||||||
|
assertEquals(5, intStates.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(0, 2, 4, 6, 8), intStates.map { it.ref.index })
|
||||||
|
assertTrue(intStates.all { it.ref.txhash == ltx.id })
|
||||||
|
val stringStates = ltx.outRefsOfType<StringTypeDummyState>()
|
||||||
|
assertEquals(5, stringStates.size)
|
||||||
|
assertEquals(listOf("0", "1", "2", "3", "4"), stringStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(1, 3, 5, 7, 9), stringStates.map { it.ref.index })
|
||||||
|
assertTrue(stringStates.all { it.ref.txhash == ltx.id })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Simple Commands of type tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intCmd1 = ltx.commandsOfType(Commands.Cmd1::class.java)
|
||||||
|
assertEquals(5, intCmd1.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intCmd1.map { (it.value as Commands.Cmd1).id })
|
||||||
|
val intCmd2 = ltx.commandsOfType<Commands.Cmd2>()
|
||||||
|
assertEquals(5, intCmd2.size)
|
||||||
|
assertEquals(listOf(0, 1, 2, 3, 4), intCmd2.map { (it.value as Commands.Cmd2).id })
|
||||||
|
val notPresentQuery = ltx.commandsOfType(FungibleAsset.Commands.Exit::class.java)
|
||||||
|
assertEquals(emptyList(), notPresentQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Filtered Input Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.filterInputs(Predicate { it.data.rem(2) == 0 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(3, intStates.size)
|
||||||
|
assertEquals(listOf(0, 2, 4), intStates.map { it.data })
|
||||||
|
val stringStates: List<StringTypeDummyState> = ltx.filterInputs { it.data == "3" }
|
||||||
|
assertEquals("3", stringStates.single().data)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Filtered InRef Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.filterInRefs(Predicate { it.data.rem(2) == 0 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(3, intStates.size)
|
||||||
|
assertEquals(listOf(0, 2, 4), intStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(ltx.inputs[0], ltx.inputs[4], ltx.inputs[8]), intStates)
|
||||||
|
val stringStates: List<StateAndRef<StringTypeDummyState>> = ltx.filterInRefs { it.data == "3" }
|
||||||
|
assertEquals("3", stringStates.single().state.data.data)
|
||||||
|
assertEquals(ltx.inputs[7], stringStates.single())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Filtered Output Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.filterOutputs(Predicate { it.data.rem(2) == 0 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(3, intStates.size)
|
||||||
|
assertEquals(listOf(0, 2, 4), intStates.map { it.data })
|
||||||
|
val stringStates: List<StringTypeDummyState> = ltx.filterOutputs { it.data == "3" }
|
||||||
|
assertEquals("3", stringStates.single().data)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Filtered OutRef Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intStates = ltx.filterOutRefs(Predicate { it.data.rem(2) == 0 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(3, intStates.size)
|
||||||
|
assertEquals(listOf(0, 2, 4), intStates.map { it.state.data.data })
|
||||||
|
assertEquals(listOf(0, 4, 8), intStates.map { it.ref.index })
|
||||||
|
assertTrue(intStates.all { it.ref.txhash == ltx.id })
|
||||||
|
val stringStates: List<StateAndRef<StringTypeDummyState>> = ltx.filterOutRefs { it.data == "3" }
|
||||||
|
assertEquals("3", stringStates.single().state.data.data)
|
||||||
|
assertEquals(7, stringStates.single().ref.index)
|
||||||
|
assertEquals(ltx.id, stringStates.single().ref.txhash)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Filtered Commands Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intCmds1 = ltx.filterCommands(Predicate { it.id.rem(2) == 0 }, Commands.Cmd1::class.java)
|
||||||
|
assertEquals(3, intCmds1.size)
|
||||||
|
assertEquals(listOf(0, 2, 4), intCmds1.map { (it.value as Commands.Cmd1).id })
|
||||||
|
val intCmds2 = ltx.filterCommands<Commands.Cmd2> { it.id == 3 }
|
||||||
|
assertEquals(3, (intCmds2.single().value as Commands.Cmd2).id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Find Input Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intState = ltx.findInput(Predicate { it.data == 4 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(ltx.getInput(8), intState)
|
||||||
|
val stringState: StringTypeDummyState = ltx.findInput { it.data == "3" }
|
||||||
|
assertEquals(ltx.getInput(7), stringState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Find InRef Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intState = ltx.findInRef(Predicate { it.data == 4 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(ltx.inRef(8), intState)
|
||||||
|
val stringState: StateAndRef<StringTypeDummyState> = ltx.findInRef { it.data == "3" }
|
||||||
|
assertEquals(ltx.inRef(7), stringState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Find Output Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intState = ltx.findOutput(Predicate { it.data == 4 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(ltx.getOutput(8), intState)
|
||||||
|
val stringState: StringTypeDummyState = ltx.findOutput { it.data == "3" }
|
||||||
|
assertEquals(ltx.getOutput(7), stringState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Find OutRef Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intState = ltx.findOutRef(Predicate { it.data == 4 }, IntTypeDummyState::class.java)
|
||||||
|
assertEquals(ltx.outRef(8), intState)
|
||||||
|
val stringState: StateAndRef<StringTypeDummyState> = ltx.findOutRef { it.data == "3" }
|
||||||
|
assertEquals(ltx.outRef(7), stringState)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Find Commands Tests`() {
|
||||||
|
val ltx = makeDummyTransaction()
|
||||||
|
val intCmd1 = ltx.findCommand(Predicate { it.id == 2 }, Commands.Cmd1::class.java)
|
||||||
|
assertEquals(ltx.getCommand(4), intCmd1)
|
||||||
|
val intCmd2 = ltx.findCommand<Commands.Cmd2> { it.id == 3 }
|
||||||
|
assertEquals(ltx.getCommand(7), intCmd2)
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ class TransactionEncumbranceTests {
|
|||||||
class DummyTimeLock : Contract {
|
class DummyTimeLock : Contract {
|
||||||
override val legalContractReference = SecureHash.sha256("DummyTimeLock")
|
override val legalContractReference = SecureHash.sha256("DummyTimeLock")
|
||||||
override fun verify(tx: LedgerTransaction) {
|
override fun verify(tx: LedgerTransaction) {
|
||||||
val timeLockInput = tx.inputs.map { it.state.data }.filterIsInstance<State>().singleOrNull() ?: return
|
val timeLockInput = tx.inputsOfType<State>().singleOrNull() ?: return
|
||||||
val time = tx.timeWindow?.untilTime ?: throw IllegalArgumentException("Transactions containing time-locks must have a time-window")
|
val time = tx.timeWindow?.untilTime ?: throw IllegalArgumentException("Transactions containing time-locks must have a time-window")
|
||||||
requireThat {
|
requireThat {
|
||||||
"the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom)
|
"the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom)
|
||||||
|
@ -61,9 +61,10 @@ class CollectSignaturesFlowTests {
|
|||||||
val flow = object : SignTransactionFlow(otherParty) {
|
val flow = object : SignTransactionFlow(otherParty) {
|
||||||
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||||
val tx = stx.tx
|
val tx = stx.tx
|
||||||
|
val ltx = tx.toLedgerTransaction(serviceHub)
|
||||||
"There should only be one output state" using (tx.outputs.size == 1)
|
"There should only be one output state" using (tx.outputs.size == 1)
|
||||||
"There should only be one output state" using (tx.inputs.isEmpty())
|
"There should only be one output state" using (tx.inputs.isEmpty())
|
||||||
val magicNumberState = tx.outputs.single().data as DummyContract.MultiOwnerState
|
val magicNumberState = ltx.outputsOfType<DummyContract.MultiOwnerState>().single()
|
||||||
"Must be 1337 or greater" using (magicNumberState.magicNumber >= 1337)
|
"Must be 1337 or greater" using (magicNumberState.magicNumber >= 1337)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,9 +119,10 @@ class CollectSignaturesFlowTests {
|
|||||||
val flow = object : SignTransactionFlow(otherParty) {
|
val flow = object : SignTransactionFlow(otherParty) {
|
||||||
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||||
val tx = stx.tx
|
val tx = stx.tx
|
||||||
|
val ltx = tx.toLedgerTransaction(serviceHub)
|
||||||
"There should only be one output state" using (tx.outputs.size == 1)
|
"There should only be one output state" using (tx.outputs.size == 1)
|
||||||
"There should only be one output state" using (tx.inputs.isEmpty())
|
"There should only be one output state" using (tx.inputs.isEmpty())
|
||||||
val magicNumberState = tx.outputs.single().data as DummyContract.MultiOwnerState
|
val magicNumberState = ltx.outputsOfType<DummyContract.MultiOwnerState>().single()
|
||||||
"Must be 1337 or greater" using (magicNumberState.magicNumber >= 1337)
|
"Must be 1337 or greater" using (magicNumberState.magicNumber >= 1337)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
|
|||||||
}
|
}
|
||||||
val copiedWireTransaction = bytes.deserialize(context = context)
|
val copiedWireTransaction = bytes.deserialize(context = context)
|
||||||
assertEquals(1, copiedWireTransaction.outputs.size)
|
assertEquals(1, copiedWireTransaction.outputs.size)
|
||||||
val contract2 = copiedWireTransaction.outputs[0].data.contract as DummyContractBackdoor
|
val contract2 = copiedWireTransaction.getOutput(0).contract as DummyContractBackdoor
|
||||||
assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
|
assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,10 +12,7 @@ import net.corda.core.node.services.ServiceType
|
|||||||
import net.corda.core.node.services.Vault.Page
|
import net.corda.core.node.services.Vault.Page
|
||||||
import net.corda.core.node.services.queryBy
|
import net.corda.core.node.services.queryBy
|
||||||
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
|
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.*
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.ProgressTracker.Step
|
import net.corda.core.utilities.ProgressTracker.Step
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
@ -416,7 +413,7 @@ object FlowCookbook {
|
|||||||
// sign it! We need to make sure the transaction represents an
|
// sign it! We need to make sure the transaction represents an
|
||||||
// agreement we actually want to enter into.
|
// agreement we actually want to enter into.
|
||||||
// DOCSTART 34
|
// DOCSTART 34
|
||||||
val outputState: DummyState = wireTx.outputs.single().data as DummyState
|
val outputState: DummyState = wireTx.outputsOfType<DummyState>().single()
|
||||||
if (outputState.magicNumber == 777) {
|
if (outputState.magicNumber == 777) {
|
||||||
// ``FlowException`` is a special exception type. It will be
|
// ``FlowException`` is a special exception type. It will be
|
||||||
// propagated back to any counterparty flows waiting for a
|
// propagated back to any counterparty flows waiting for a
|
||||||
@ -548,7 +545,7 @@ object FlowCookbook {
|
|||||||
val signTransactionFlow: SignTransactionFlow = object : SignTransactionFlow(counterparty) {
|
val signTransactionFlow: SignTransactionFlow = object : SignTransactionFlow(counterparty) {
|
||||||
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||||
// Any additional checking we see fit...
|
// Any additional checking we see fit...
|
||||||
val outputState = stx.tx.outputs.single().data as DummyState
|
val outputState = stx.tx.outputsOfType<DummyState>().single()
|
||||||
assert(outputState.magicNumber == 777)
|
assert(outputState.magicNumber == 777)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
|
|||||||
"Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty())
|
"Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty())
|
||||||
"Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1)
|
"Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1)
|
||||||
}
|
}
|
||||||
val issued = tx.outputs[0].data as TradeApprovalContract.State
|
val issued = tx.outputsOfType<TradeApprovalContract.State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey))
|
"Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey))
|
||||||
"Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW)
|
"Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW)
|
||||||
|
@ -192,7 +192,7 @@ class UniversalContract : Contract {
|
|||||||
|
|
||||||
when (value) {
|
when (value) {
|
||||||
is Commands.Action -> {
|
is Commands.Action -> {
|
||||||
val inState = tx.inputs.single().state.data as State
|
val inState = tx.inputsOfType<State>().single()
|
||||||
val arr = when (inState.details) {
|
val arr = when (inState.details) {
|
||||||
is Actions -> inState.details
|
is Actions -> inState.details
|
||||||
is RollOut -> reduceRollOut(inState.details)
|
is RollOut -> reduceRollOut(inState.details)
|
||||||
@ -222,7 +222,7 @@ class UniversalContract : Contract {
|
|||||||
|
|
||||||
when (tx.outputs.size) {
|
when (tx.outputs.size) {
|
||||||
1 -> {
|
1 -> {
|
||||||
val outState = tx.outputs.single().data as State
|
val outState = tx.outputsOfType<State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"output state must match action result state" using (arrangement.equals(outState.details))
|
"output state must match action result state" using (arrangement.equals(outState.details))
|
||||||
"output state must match action result state" using (rest == zero)
|
"output state must match action result state" using (rest == zero)
|
||||||
@ -230,7 +230,7 @@ class UniversalContract : Contract {
|
|||||||
}
|
}
|
||||||
0 -> throw IllegalArgumentException("must have at least one out state")
|
0 -> throw IllegalArgumentException("must have at least one out state")
|
||||||
else -> {
|
else -> {
|
||||||
val allContracts = And(tx.outputs.map { (it.data as State).details }.toSet())
|
val allContracts = And(tx.outputsOfType<State>().map { it.details }.toSet())
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"output states must match action result state" using (arrangement.equals(allContracts))
|
"output states must match action result state" using (arrangement.equals(allContracts))
|
||||||
@ -240,15 +240,15 @@ class UniversalContract : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Commands.Issue -> {
|
is Commands.Issue -> {
|
||||||
val outState = tx.outputs.single().data as State
|
val outState = tx.outputsOfType<State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by all liable parties" using (liableParties(outState.details).all { it in cmd.signers })
|
"the transaction is signed by all liable parties" using (liableParties(outState.details).all { it in cmd.signers })
|
||||||
"the transaction has no input states" using tx.inputs.isEmpty()
|
"the transaction has no input states" using tx.inputs.isEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Commands.Move -> {
|
is Commands.Move -> {
|
||||||
val inState = tx.inputs.single().state.data as State
|
val inState = tx.inputsOfType<State>().single()
|
||||||
val outState = tx.outputs.single().data as State
|
val outState = tx.outputsOfType<State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by all liable parties" using
|
"the transaction is signed by all liable parties" using
|
||||||
(liableParties(outState.details).all { it in cmd.signers })
|
(liableParties(outState.details).all { it in cmd.signers })
|
||||||
@ -257,13 +257,13 @@ class UniversalContract : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Commands.Fix -> {
|
is Commands.Fix -> {
|
||||||
val inState = tx.inputs.single().state.data as State
|
val inState = tx.inputsOfType<State>().single()
|
||||||
val arr = when (inState.details) {
|
val arr = when (inState.details) {
|
||||||
is Actions -> inState.details
|
is Actions -> inState.details
|
||||||
is RollOut -> reduceRollOut(inState.details)
|
is RollOut -> reduceRollOut(inState.details)
|
||||||
else -> throw IllegalArgumentException("Unexpected arrangement, " + tx.inputs.single())
|
else -> throw IllegalArgumentException("Unexpected arrangement, " + tx.inputs.single())
|
||||||
}
|
}
|
||||||
val outState = tx.outputs.single().data as State
|
val outState = tx.outputsOfType<State>().single()
|
||||||
|
|
||||||
val unusedFixes = value.fixes.map { it.of }.toMutableSet()
|
val unusedFixes = value.fixes.map { it.of }.toMutableSet()
|
||||||
val expectedArr = replaceFixing(tx, arr,
|
val expectedArr = replaceFixing(tx, arr,
|
||||||
|
@ -13,13 +13,13 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentState
|
import net.corda.core.schemas.PersistentState
|
||||||
import net.corda.core.schemas.QueryableState
|
import net.corda.core.schemas.QueryableState
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.internal.Emoji
|
|
||||||
import net.corda.schemas.CommercialPaperSchemaV1
|
import net.corda.schemas.CommercialPaperSchemaV1
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -172,7 +172,7 @@ class CommercialPaper : Contract {
|
|||||||
val timeWindow = tx.timeWindow
|
val timeWindow = tx.timeWindow
|
||||||
|
|
||||||
val input = inputs.single()
|
val input = inputs.single()
|
||||||
val received = tx.outputs.map { it.data }.sumCashBy(input.owner)
|
val received = tx.outputStates.sumCashBy(input.owner)
|
||||||
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
|
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
|
||||||
requireThat {
|
requireThat {
|
||||||
"the paper must have matured" using (time >= input.maturityDate)
|
"the paper must have matured" using (time >= input.maturityDate)
|
||||||
|
@ -7,10 +7,10 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.crypto.testing.NULL_PARTY
|
import net.corda.core.crypto.testing.NULL_PARTY
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.internal.Emoji
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class CommercialPaperLegacy : Contract {
|
|||||||
is Commands.Redeem -> {
|
is Commands.Redeem -> {
|
||||||
// Redemption of the paper requires movement of on-ledger cash.
|
// Redemption of the paper requires movement of on-ledger cash.
|
||||||
val input = inputs.single()
|
val input = inputs.single()
|
||||||
val received = tx.outputs.map { it.data }.sumCashBy(input.owner)
|
val received = tx.outputStates.sumCashBy(input.owner)
|
||||||
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
|
val time = timeWindow?.fromTime ?: throw IllegalArgumentException("Redemptions must have a time-window")
|
||||||
requireThat {
|
requireThat {
|
||||||
"the paper must have matured" using (time >= input.maturityDate)
|
"the paper must have matured" using (time >= input.maturityDate)
|
||||||
|
@ -14,10 +14,10 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.internal.Emoji
|
|
||||||
import net.corda.core.utilities.NonEmptySet
|
import net.corda.core.utilities.NonEmptySet
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -159,7 +159,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
// Move (signed by B)
|
// Move (signed by B)
|
||||||
//
|
//
|
||||||
// That would pass this check. Ensuring they do not is best addressed in the transaction generation stage.
|
// That would pass this check. Ensuring they do not is best addressed in the transaction generation stage.
|
||||||
val assetStates = tx.outputs.map { it.data }.filterIsInstance<FungibleAsset<*>>()
|
val assetStates = tx.outputsOfType<FungibleAsset<*>>()
|
||||||
val acceptableAssetStates = assetStates
|
val acceptableAssetStates = assetStates
|
||||||
// TODO: This filter is nonsense, because it just checks there is an asset contract loaded, we need to
|
// TODO: This filter is nonsense, because it just checks there is an asset contract loaded, we need to
|
||||||
// verify the asset contract is the asset contract we expect.
|
// verify the asset contract is the asset contract we expect.
|
||||||
|
@ -6,8 +6,8 @@ import net.corda.core.contracts.*
|
|||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -49,10 +49,7 @@ object IssuerFlow {
|
|||||||
return sendAndReceive<AbstractCashFlow.Result>(issuerBankParty, issueRequest).unwrap { res ->
|
return sendAndReceive<AbstractCashFlow.Result>(issuerBankParty, issueRequest).unwrap { res ->
|
||||||
val tx = res.stx.tx
|
val tx = res.stx.tx
|
||||||
val expectedAmount = Amount(amount.quantity, Issued(issuerBankParty.ref(issueToPartyRef), amount.token))
|
val expectedAmount = Amount(amount.quantity, Issued(issuerBankParty.ref(issueToPartyRef), amount.token))
|
||||||
val cashOutputs = tx.outputs
|
val cashOutputs = tx.filterOutputs<Cash.State> { state -> state.owner == res.recipient }
|
||||||
.map { it.data}
|
|
||||||
.filterIsInstance<Cash.State>()
|
|
||||||
.filter { state -> state.owner == res.recipient }
|
|
||||||
require(cashOutputs.size == 1) { "Require a single cash output paying ${res.recipient}, found ${tx.outputs}" }
|
require(cashOutputs.size == 1) { "Require a single cash output paying ${res.recipient}, found ${tx.outputs}" }
|
||||||
require(cashOutputs.single().amount == expectedAmount) { "Require payment of $expectedAmount"}
|
require(cashOutputs.single().amount == expectedAmount) { "Require payment of $expectedAmount"}
|
||||||
res
|
res
|
||||||
|
@ -8,11 +8,11 @@ import net.corda.core.identity.AbstractParty
|
|||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -85,7 +85,7 @@ object TwoPartyTradeFlow {
|
|||||||
// DOCSTART 5
|
// DOCSTART 5
|
||||||
val signTransactionFlow = object : SignTransactionFlow(otherParty, VERIFYING_AND_SIGNING.childProgressTracker()) {
|
val signTransactionFlow = object : SignTransactionFlow(otherParty, VERIFYING_AND_SIGNING.childProgressTracker()) {
|
||||||
override fun checkTransaction(stx: SignedTransaction) {
|
override fun checkTransaction(stx: SignedTransaction) {
|
||||||
if (stx.tx.outputs.map { it.data }.sumCashBy(me).withoutIssuer() != price)
|
if (stx.tx.outputStates.sumCashBy(me).withoutIssuer() != price)
|
||||||
throw FlowException("Transaction is not sending us the right amount of cash")
|
throw FlowException("Transaction is not sending us the right amount of cash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class CashTests : TestDependencyInjectionBase() {
|
|||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
|
||||||
}.toWireTransaction()
|
}.toWireTransaction()
|
||||||
assertTrue(tx.inputs.isEmpty())
|
assertTrue(tx.inputs.isEmpty())
|
||||||
val s = tx.outputs[0].data as Cash.State
|
val s = tx.outputsOfType<Cash.State>().single()
|
||||||
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
|
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
|
||||||
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
|
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
|
||||||
assertEquals(AnonymousParty(DUMMY_PUBKEY_1), s.owner)
|
assertEquals(AnonymousParty(DUMMY_PUBKEY_1), s.owner)
|
||||||
@ -514,7 +514,7 @@ class CashTests : TestDependencyInjectionBase() {
|
|||||||
val wtx = makeExit(50.DOLLARS, MEGA_CORP, 1)
|
val wtx = makeExit(50.DOLLARS, MEGA_CORP, 1)
|
||||||
assertEquals(WALLET[0].ref, wtx.inputs[0])
|
assertEquals(WALLET[0].ref, wtx.inputs[0])
|
||||||
assertEquals(1, wtx.outputs.size)
|
assertEquals(1, wtx.outputs.size)
|
||||||
assertEquals(WALLET[0].state.data.copy(amount = WALLET[0].state.data.amount.splitEvenly(2).first()), wtx.outputs[0].data)
|
assertEquals(WALLET[0].state.data.copy(amount = WALLET[0].state.data.amount.splitEvenly(2).first()), wtx.getOutput(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -574,7 +574,7 @@ class CashTests : TestDependencyInjectionBase() {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||||
assertEquals(vaultState.ref, wtx.inputs[0])
|
assertEquals(vaultState.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
|
assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1), wtx.getOutput(0))
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ class CashTests : TestDependencyInjectionBase() {
|
|||||||
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
|
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
|
||||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.getOutput(0))
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -639,7 +639,7 @@ class CashTests : TestDependencyInjectionBase() {
|
|||||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||||
assertEquals(vaultState2.ref, wtx.inputs[2])
|
assertEquals(vaultState2.ref, wtx.inputs[2])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
||||||
assertEquals(vaultState2.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
|
assertEquals(vaultState2.state.data.copy(owner = THEIR_IDENTITY_1), wtx.getOutput(0))
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ class ObligationTests {
|
|||||||
beneficiary = CHARLIE,
|
beneficiary = CHARLIE,
|
||||||
template = megaCorpDollarSettlement
|
template = megaCorpDollarSettlement
|
||||||
)
|
)
|
||||||
assertEquals(tx.outputs[0].data, expected)
|
assertEquals(tx.getOutput(0), expected)
|
||||||
assertTrue(tx.commands[0].value is Obligation.Commands.Issue)
|
assertTrue(tx.commands[0].value is Obligation.Commands.Issue)
|
||||||
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
|
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
|
||||||
resetTestSerialization()
|
resetTestSerialization()
|
||||||
@ -250,7 +250,7 @@ class ObligationTests {
|
|||||||
}.toWireTransaction()
|
}.toWireTransaction()
|
||||||
assertEquals(1, tx.outputs.size)
|
assertEquals(1, tx.outputs.size)
|
||||||
|
|
||||||
val actual = tx.outputs[0].data
|
val actual = tx.getOutput(0)
|
||||||
assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), actual)
|
assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ class ObligationTests {
|
|||||||
}.toWireTransaction()
|
}.toWireTransaction()
|
||||||
assertEquals(1, tx.outputs.size)
|
assertEquals(1, tx.outputs.size)
|
||||||
val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity)
|
val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity)
|
||||||
val actual = tx.outputs[0].data
|
val actual = tx.getOutput(0)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +305,7 @@ class ObligationTests {
|
|||||||
stx = notaryServices.addSignature(ptx)
|
stx = notaryServices.addSignature(ptx)
|
||||||
|
|
||||||
assertEquals(1, stx.tx.outputs.size)
|
assertEquals(1, stx.tx.outputs.size)
|
||||||
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), stx.tx.outputs[0].data)
|
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), stx.tx.getOutput(0))
|
||||||
stx.verifyRequiredSignatures()
|
stx.verifyRequiredSignatures()
|
||||||
|
|
||||||
// And set it back
|
// And set it back
|
||||||
@ -316,7 +316,7 @@ class ObligationTests {
|
|||||||
ptx = miniCorpServices.signInitialTransaction(tx)
|
ptx = miniCorpServices.signInitialTransaction(tx)
|
||||||
stx = notaryServices.addSignature(ptx)
|
stx = notaryServices.addSignature(ptx)
|
||||||
assertEquals(1, stx.tx.outputs.size)
|
assertEquals(1, stx.tx.outputs.size)
|
||||||
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), stx.tx.outputs[0].data)
|
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), stx.tx.getOutput(0))
|
||||||
stx.verifyRequiredSignatures()
|
stx.verifyRequiredSignatures()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class CashExitFlowTests {
|
|||||||
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
|
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
|
||||||
assertEquals(1, exitTx.inputs.size)
|
assertEquals(1, exitTx.inputs.size)
|
||||||
assertEquals(1, exitTx.outputs.size)
|
assertEquals(1, exitTx.outputs.size)
|
||||||
val output = exitTx.outputs.map { it.data }.filterIsInstance<Cash.State>().single()
|
val output = exitTx.outputsOfType<Cash.State>().single()
|
||||||
assertEquals(expected, output.amount)
|
assertEquals(expected, output.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class CashIssueFlowTests {
|
|||||||
notary)).resultFuture
|
notary)).resultFuture
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val issueTx = future.getOrThrow().stx
|
val issueTx = future.getOrThrow().stx
|
||||||
val output = issueTx.tx.outputs.single().data as Cash.State
|
val output = issueTx.tx.outputsOfType<Cash.State>().single()
|
||||||
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), output.amount)
|
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), output.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +457,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
|||||||
// Retrieve unspent and unlocked cash states that meet our spending criteria.
|
// Retrieve unspent and unlocked cash states that meet our spending criteria.
|
||||||
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
|
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
|
||||||
return OnLedgerAsset.generateSpend(tx, amount, to, acceptableCoins,
|
return OnLedgerAsset.generateSpend(tx, amount, to, acceptableCoins,
|
||||||
{ state, amount, owner -> deriveState(state, amount, owner) },
|
{ state, quantity, owner -> deriveState(state, quantity, owner) },
|
||||||
{ Cash().generateMoveCommand() })
|
{ Cash().generateMoveCommand() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,9 +466,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
|
internal fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
|
||||||
val ourNewStates = tx.outputs.
|
val ourNewStates = tx.filterOutRefs<ContractState> { isRelevant(it, ourKeys) }
|
||||||
filter { isRelevant(it.data, ourKeys) }.
|
|
||||||
map { tx.outRef<ContractState>(it.data) }
|
|
||||||
|
|
||||||
// Retrieve all unconsumed states for this transaction's inputs
|
// Retrieve all unconsumed states for this transaction's inputs
|
||||||
val consumedStates = HashSet<StateAndRef<ContractState>>()
|
val consumedStates = HashSet<StateAndRef<ContractState>>()
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.testing.contracts.DummyContract
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
|
import net.corda.core.flows.NotaryChangeFlow
|
||||||
|
import net.corda.core.flows.StateReplacementException
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.flows.NotaryChangeFlow
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.core.flows.StateReplacementException
|
|
||||||
import net.corda.node.internal.AbstractNode
|
import net.corda.node.internal.AbstractNode
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.getTestPartyAndCertificate
|
import net.corda.testing.getTestPartyAndCertificate
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
@ -109,8 +109,8 @@ class NotaryChangeTests {
|
|||||||
val notaryChangeTx = clientNodeA.services.validatedTransactions.getTransaction(newState.ref.txhash)!!.tx
|
val notaryChangeTx = clientNodeA.services.validatedTransactions.getTransaction(newState.ref.txhash)!!.tx
|
||||||
|
|
||||||
// Check that all encumbrances have been propagated to the outputs
|
// Check that all encumbrances have been propagated to the outputs
|
||||||
val originalOutputs = issueTx.outputs.map { it.data }
|
val originalOutputs = issueTx.outputStates
|
||||||
val newOutputs = notaryChangeTx.outputs.map { it.data }
|
val newOutputs = notaryChangeTx.outputStates
|
||||||
assertTrue(originalOutputs.minus(newOutputs).isEmpty())
|
assertTrue(originalOutputs.minus(newOutputs).isEmpty())
|
||||||
|
|
||||||
// Check that encumbrance links aren't broken after notary change
|
// Check that encumbrance links aren't broken after notary change
|
||||||
|
@ -125,7 +125,7 @@ fun recipient(rpc: CordaRPCOps) {
|
|||||||
val wtx = stx.tx
|
val wtx = stx.tx
|
||||||
if (wtx.attachments.isNotEmpty()) {
|
if (wtx.attachments.isNotEmpty()) {
|
||||||
if (wtx.outputs.isNotEmpty()) {
|
if (wtx.outputs.isNotEmpty()) {
|
||||||
val state = wtx.outputs.map { it.data }.filterIsInstance<AttachmentContract.State>().single()
|
val state = wtx.outputsOfType<AttachmentContract.State>().single()
|
||||||
require(rpc.attachmentExists(state.hash))
|
require(rpc.attachmentExists(state.hash))
|
||||||
|
|
||||||
// Download the attachment via the Web endpoint.
|
// Download the attachment via the Web endpoint.
|
||||||
@ -173,7 +173,7 @@ class AttachmentContract : Contract {
|
|||||||
get() = SecureHash.zeroHash // TODO not implemented
|
get() = SecureHash.zeroHash // TODO not implemented
|
||||||
|
|
||||||
override fun verify(tx: LedgerTransaction) {
|
override fun verify(tx: LedgerTransaction) {
|
||||||
val state = tx.outputs.map { it.data }.filterIsInstance<AttachmentContract.State>().single()
|
val state = tx.outputsOfType<AttachmentContract.State>().single()
|
||||||
val attachment = tx.attachments.single()
|
val attachment = tx.attachments.single()
|
||||||
require(state.hash == attachment.id)
|
require(state.hash == attachment.id)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package net.corda.irs.contract
|
|||||||
|
|
||||||
import net.corda.contracts.*
|
import net.corda.contracts.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -246,7 +246,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
|||||||
* Utility so I don't have to keep typing this.
|
* Utility so I don't have to keep typing this.
|
||||||
*/
|
*/
|
||||||
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State {
|
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State {
|
||||||
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
|
return generateIRSTxn(irsSelector).tx.outputsOfType<InterestRateSwap.State>().single()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -300,7 +300,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
|||||||
var previousTXN = generateIRSTxn(1)
|
var previousTXN = generateIRSTxn(1)
|
||||||
previousTXN.toLedgerTransaction(services).verify()
|
previousTXN.toLedgerTransaction(services).verify()
|
||||||
services.recordTransactions(previousTXN)
|
services.recordTransactions(previousTXN)
|
||||||
fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
|
fun currentIRS() = previousTXN.tx.outputsOfType<InterestRateSwap.State>().single()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
|
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
|
||||||
|
@ -27,8 +27,8 @@ import javafx.util.Duration
|
|||||||
import net.corda.client.jfx.model.*
|
import net.corda.client.jfx.model.*
|
||||||
import net.corda.client.jfx.utils.*
|
import net.corda.client.jfx.utils.*
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.explorer.formatters.PartyNameFormatter
|
import net.corda.explorer.formatters.PartyNameFormatter
|
||||||
import net.corda.explorer.model.CordaView
|
import net.corda.explorer.model.CordaView
|
||||||
@ -77,7 +77,7 @@ class Network : CordaView() {
|
|||||||
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.map { it.stateAndRef.state.data }.getParties()
|
.map { it.stateAndRef.state.data }.getParties()
|
||||||
val outputParties = it.transaction.tx.outputs.map { it.data }.observable().getParties()
|
val outputParties = it.transaction.tx.outputStates.observable().getParties()
|
||||||
val signingParties = it.transaction.sigs.map { getModel<NetworkIdentityModel>().lookup(it.by) }
|
val signingParties = it.transaction.sigs.map { getModel<NetworkIdentityModel>().lookup(it.by) }
|
||||||
// Input parties fire a bullets to all output parties, and to the signing parties. !! This is a rough guess of how the message moves in the network.
|
// Input parties fire a bullets to all output parties, and to the signing parties. !! This is a rough guess of how the message moves in the network.
|
||||||
// TODO : Expose artemis queue to get real message information.
|
// TODO : Expose artemis queue to get real message information.
|
||||||
|
@ -22,7 +22,10 @@ import net.corda.client.jfx.utils.map
|
|||||||
import net.corda.client.jfx.utils.sequence
|
import net.corda.client.jfx.utils.sequence
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.commonName
|
||||||
|
import net.corda.core.crypto.toBase58String
|
||||||
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.explorer.AmountDiff
|
import net.corda.explorer.AmountDiff
|
||||||
@ -124,7 +127,7 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity,
|
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity,
|
||||||
reportingExchange,
|
reportingExchange,
|
||||||
resolved.map { it.state.data }.lift(),
|
resolved.map { it.state.data }.lift(),
|
||||||
it.transaction.tx.outputs.map { it.data }.lift())
|
it.transaction.tx.outputStates.lift())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user