mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
Add state query methods to LedgerTransaction.
Update to use LedgerTransaction api Push query output logic onto BaseTransaction and update usages where possible Migrate a few more uses Address some PR comments Address some PR comments Fixup after rebase
This commit is contained in:
parent
fe9db6f1f7
commit
9a02a27619
@ -123,7 +123,7 @@ sealed class TransactionType {
|
||||
*/
|
||||
private fun verifyContracts(tx: LedgerTransaction) {
|
||||
// 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) {
|
||||
try {
|
||||
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")
|
||||
}
|
||||
}
|
||||
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) }
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
|
||||
@JvmStatic
|
||||
fun verify(tx: LedgerTransaction) {
|
||||
// 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
|
||||
|
@ -133,7 +133,7 @@ open class FinalityFlow(val transactions: Iterable<SignedTransaction>,
|
||||
* overriding functions.
|
||||
*/
|
||||
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.identity.Party
|
||||
import net.corda.core.indexOfOrThrow
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* 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 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 java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
/**
|
||||
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
|
||||
@ -42,8 +43,15 @@ class LedgerTransaction(
|
||||
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")
|
||||
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:
|
||||
@ -100,8 +108,8 @@ class LedgerTransaction(
|
||||
*/
|
||||
// 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 inputs = inputsOfType(ofType)
|
||||
val outputs = outputsOfType(ofType)
|
||||
|
||||
val inGroups: Map<K, List<T>> = inputs.groupBy(selector)
|
||||
val outGroups: Map<K, List<T>> = outputs.groupBy(selector)
|
||||
@ -136,4 +144,166 @@ class LedgerTransaction(
|
||||
// DOCSTART 3
|
||||
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
|
||||
// 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.keys
|
||||
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.serialization.*
|
||||
import net.corda.core.serialization.SerializationDefaults.P2P_CONTEXT
|
||||
import net.corda.core.serialization.SerializationDefaults.SERIALIZATION_FACTORY
|
||||
import net.corda.core.internal.Emoji
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
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
|
||||
* 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 {
|
||||
override val legalContractReference = SecureHash.sha256("DummyTimeLock")
|
||||
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")
|
||||
requireThat {
|
||||
"the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom)
|
||||
|
@ -61,9 +61,10 @@ class CollectSignaturesFlowTests {
|
||||
val flow = object : SignTransactionFlow(otherParty) {
|
||||
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||
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.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)
|
||||
}
|
||||
}
|
||||
@ -118,9 +119,10 @@ class CollectSignaturesFlowTests {
|
||||
val flow = object : SignTransactionFlow(otherParty) {
|
||||
@Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||
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.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)
|
||||
}
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
|
||||
}
|
||||
val copiedWireTransaction = bytes.deserialize(context = context)
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -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.queryBy
|
||||
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.transactions.*
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.ProgressTracker.Step
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
@ -416,7 +413,7 @@ object FlowCookbook {
|
||||
// sign it! We need to make sure the transaction represents an
|
||||
// agreement we actually want to enter into.
|
||||
// DOCSTART 34
|
||||
val outputState: DummyState = wireTx.outputs.single().data as DummyState
|
||||
val outputState: DummyState = wireTx.outputsOfType<DummyState>().single()
|
||||
if (outputState.magicNumber == 777) {
|
||||
// ``FlowException`` is a special exception type. It will be
|
||||
// propagated back to any counterparty flows waiting for a
|
||||
@ -548,7 +545,7 @@ object FlowCookbook {
|
||||
val signTransactionFlow: SignTransactionFlow = object : SignTransactionFlow(counterparty) {
|
||||
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -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 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 {
|
||||
"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)
|
||||
|
@ -192,7 +192,7 @@ class UniversalContract : Contract {
|
||||
|
||||
when (value) {
|
||||
is Commands.Action -> {
|
||||
val inState = tx.inputs.single().state.data as State
|
||||
val inState = tx.inputsOfType<State>().single()
|
||||
val arr = when (inState.details) {
|
||||
is Actions -> inState.details
|
||||
is RollOut -> reduceRollOut(inState.details)
|
||||
@ -222,7 +222,7 @@ class UniversalContract : Contract {
|
||||
|
||||
when (tx.outputs.size) {
|
||||
1 -> {
|
||||
val outState = tx.outputs.single().data as State
|
||||
val outState = tx.outputsOfType<State>().single()
|
||||
requireThat {
|
||||
"output state must match action result state" using (arrangement.equals(outState.details))
|
||||
"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")
|
||||
else -> {
|
||||
val allContracts = And(tx.outputs.map { (it.data as State).details }.toSet())
|
||||
val allContracts = And(tx.outputsOfType<State>().map { it.details }.toSet())
|
||||
|
||||
requireThat {
|
||||
"output states must match action result state" using (arrangement.equals(allContracts))
|
||||
@ -240,15 +240,15 @@ class UniversalContract : Contract {
|
||||
}
|
||||
}
|
||||
is Commands.Issue -> {
|
||||
val outState = tx.outputs.single().data as State
|
||||
val outState = tx.outputsOfType<State>().single()
|
||||
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().state.data as State
|
||||
val outState = tx.outputs.single().data as State
|
||||
val inState = tx.inputsOfType<State>().single()
|
||||
val outState = tx.outputsOfType<State>().single()
|
||||
requireThat {
|
||||
"the transaction is signed by all liable parties" using
|
||||
(liableParties(outState.details).all { it in cmd.signers })
|
||||
@ -257,13 +257,13 @@ class UniversalContract : Contract {
|
||||
}
|
||||
}
|
||||
is Commands.Fix -> {
|
||||
val inState = tx.inputs.single().state.data as State
|
||||
val inState = tx.inputsOfType<State>().single()
|
||||
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().data as State
|
||||
val outState = tx.outputsOfType<State>().single()
|
||||
|
||||
val unusedFixes = value.fixes.map { it.of }.toMutableSet()
|
||||
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.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.node.services.VaultService
|
||||
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
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -172,7 +172,7 @@ class CommercialPaper : Contract {
|
||||
val timeWindow = tx.timeWindow
|
||||
|
||||
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")
|
||||
requireThat {
|
||||
"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.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.Emoji
|
||||
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
|
||||
import java.util.*
|
||||
|
||||
@ -81,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.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")
|
||||
requireThat {
|
||||
"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.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.Emoji
|
||||
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
|
||||
import net.corda.core.utilities.seconds
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
@ -159,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.map { it.data }.filterIsInstance<FungibleAsset<*>>()
|
||||
val assetStates = tx.outputsOfType<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.
|
||||
|
@ -6,8 +6,8 @@ import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import java.util.*
|
||||
@ -49,10 +49,7 @@ object IssuerFlow {
|
||||
return sendAndReceive<AbstractCashFlow.Result>(issuerBankParty, issueRequest).unwrap { res ->
|
||||
val tx = res.stx.tx
|
||||
val expectedAmount = Amount(amount.quantity, Issued(issuerBankParty.ref(issueToPartyRef), amount.token))
|
||||
val cashOutputs = tx.outputs
|
||||
.map { it.data}
|
||||
.filterIsInstance<Cash.State>()
|
||||
.filter { state -> state.owner == res.recipient }
|
||||
val cashOutputs = tx.filterOutputs<Cash.State> { state -> state.owner == res.recipient }
|
||||
require(cashOutputs.size == 1) { "Require a single cash output paying ${res.recipient}, found ${tx.outputs}" }
|
||||
require(cashOutputs.single().amount == expectedAmount) { "Require payment of $expectedAmount"}
|
||||
res
|
||||
|
@ -8,11 +8,11 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.utilities.unwrap
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
@ -85,7 +85,7 @@ object TwoPartyTradeFlow {
|
||||
// DOCSTART 5
|
||||
val signTransactionFlow = object : SignTransactionFlow(otherParty, VERIFYING_AND_SIGNING.childProgressTracker()) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}.toWireTransaction()
|
||||
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(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
|
||||
assertEquals(AnonymousParty(DUMMY_PUBKEY_1), s.owner)
|
||||
@ -514,7 +514,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
val wtx = makeExit(50.DOLLARS, MEGA_CORP, 1)
|
||||
assertEquals(WALLET[0].ref, wtx.inputs[0])
|
||||
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")
|
||||
val vaultState = vaultStatesUnconsumed.elementAt(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])
|
||||
}
|
||||
}
|
||||
@ -618,7 +618,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
|
||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||
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])
|
||||
}
|
||||
}
|
||||
@ -639,7 +639,7 @@ class CashTests : TestDependencyInjectionBase() {
|
||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||
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(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])
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class ObligationTests {
|
||||
beneficiary = CHARLIE,
|
||||
template = megaCorpDollarSettlement
|
||||
)
|
||||
assertEquals(tx.outputs[0].data, expected)
|
||||
assertEquals(tx.getOutput(0), expected)
|
||||
assertTrue(tx.commands[0].value is Obligation.Commands.Issue)
|
||||
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
|
||||
resetTestSerialization()
|
||||
@ -250,7 +250,7 @@ class ObligationTests {
|
||||
}.toWireTransaction()
|
||||
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)
|
||||
}
|
||||
|
||||
@ -277,7 +277,7 @@ class ObligationTests {
|
||||
}.toWireTransaction()
|
||||
assertEquals(1, tx.outputs.size)
|
||||
val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity)
|
||||
val actual = tx.outputs[0].data
|
||||
val actual = tx.getOutput(0)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@ -305,7 +305,7 @@ class ObligationTests {
|
||||
stx = notaryServices.addSignature(ptx)
|
||||
|
||||
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()
|
||||
|
||||
// And set it back
|
||||
@ -316,7 +316,7 @@ class ObligationTests {
|
||||
ptx = miniCorpServices.signInitialTransaction(tx)
|
||||
stx = notaryServices.addSignature(ptx)
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ class CashExitFlowTests {
|
||||
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
|
||||
assertEquals(1, exitTx.inputs.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)
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ class CashIssueFlowTests {
|
||||
notary)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -457,7 +457,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
// Retrieve unspent and unlocked cash states that meet our spending criteria.
|
||||
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
|
||||
return OnLedgerAsset.generateSpend(tx, amount, to, acceptableCoins,
|
||||
{ state, amount, owner -> deriveState(state, amount, owner) },
|
||||
{ state, quantity, owner -> deriveState(state, quantity, owner) },
|
||||
{ Cash().generateMoveCommand() })
|
||||
}
|
||||
|
||||
@ -466,9 +466,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
|
||||
val ourNewStates = tx.outputs.
|
||||
filter { isRelevant(it.data, ourKeys) }.
|
||||
map { tx.outRef<ContractState>(it.data) }
|
||||
val ourNewStates = tx.filterOutRefs<ContractState> { isRelevant(it, ourKeys) }
|
||||
|
||||
// Retrieve all unconsumed states for this transaction's inputs
|
||||
val consumedStates = HashSet<StateAndRef<ContractState>>()
|
||||
|
@ -1,19 +1,19 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
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.identity.Party
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.flows.NotaryChangeFlow
|
||||
import net.corda.core.flows.StateReplacementException
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
@ -109,8 +109,8 @@ class NotaryChangeTests {
|
||||
val notaryChangeTx = clientNodeA.services.validatedTransactions.getTransaction(newState.ref.txhash)!!.tx
|
||||
|
||||
// Check that all encumbrances have been propagated to the outputs
|
||||
val originalOutputs = issueTx.outputs.map { it.data }
|
||||
val newOutputs = notaryChangeTx.outputs.map { it.data }
|
||||
val originalOutputs = issueTx.outputStates
|
||||
val newOutputs = notaryChangeTx.outputStates
|
||||
assertTrue(originalOutputs.minus(newOutputs).isEmpty())
|
||||
|
||||
// Check that encumbrance links aren't broken after notary change
|
||||
|
@ -125,7 +125,7 @@ fun recipient(rpc: CordaRPCOps) {
|
||||
val wtx = stx.tx
|
||||
if (wtx.attachments.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))
|
||||
|
||||
// Download the attachment via the Web endpoint.
|
||||
@ -173,7 +173,7 @@ class AttachmentContract : Contract {
|
||||
get() = SecureHash.zeroHash // TODO not implemented
|
||||
|
||||
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()
|
||||
require(state.hash == attachment.id)
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ package net.corda.irs.contract
|
||||
|
||||
import net.corda.contracts.*
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.junit.Test
|
||||
@ -246,7 +246,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
||||
* Utility so I don't have to keep typing this.
|
||||
*/
|
||||
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)
|
||||
previousTXN.toLedgerTransaction(services).verify()
|
||||
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) {
|
||||
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.utils.*
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.explorer.formatters.PartyNameFormatter
|
||||
import net.corda.explorer.model.CordaView
|
||||
@ -77,7 +77,7 @@ class Network : CordaView() {
|
||||
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
||||
.filterNotNull()
|
||||
.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) }
|
||||
// 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.
|
||||
|
@ -22,7 +22,10 @@ import net.corda.client.jfx.utils.map
|
||||
import net.corda.client.jfx.utils.sequence
|
||||
import net.corda.contracts.asset.Cash
|
||||
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.node.NodeInfo
|
||||
import net.corda.explorer.AmountDiff
|
||||
@ -124,7 +127,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity,
|
||||
reportingExchange,
|
||||
resolved.map { it.state.data }.lift(),
|
||||
it.transaction.tx.outputs.map { it.data }.lift())
|
||||
it.transaction.tx.outputStates.lift())
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user