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:
Matthew Nesbit 2017-07-21 16:13:18 +01:00 committed by GitHub
commit 08d2e351d2
29 changed files with 650 additions and 84 deletions

View File

@ -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()
} }
} }

View File

@ -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) }
} }

View File

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

View File

@ -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 }
} }
/** /**

View File

@ -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)
}
}

View File

@ -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)
}
} }

View File

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

View File

@ -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)
}
}

View File

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

View File

@ -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)
} }
} }

View File

@ -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))
} }

View File

@ -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)
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")
} }
} }

View File

@ -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])
} }
} }

View File

@ -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()
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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>>()

View File

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

View File

@ -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)
} }

View File

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

View File

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

View File

@ -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())
) )
} }