ContractState now references contract by class name (#1407)

* ContractState's contract type has been moved to TransactionState and is now a string representing the class name of the contract class to allow classloading of arbitrary contracts from custom classloaders.

* Upgraded isolated JAR to new version.
This commit is contained in:
Clinton
2017-09-11 16:44:18 +01:00
committed by GitHub
parent 10c4d46b97
commit bc6628a072
81 changed files with 669 additions and 685 deletions

2
.idea/compiler.xml generated
View File

@ -109,4 +109,4 @@
<component name="JavacSettings"> <component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" /> <option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
</component> </component>
</project> </project>

View File

@ -0,0 +1,27 @@
package net.corda.core.contracts
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
/**
* A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
* file that the program can use to persist data across transactions. States are immutable: once created they are never
* updated, instead, any changes must generate a new successor state. States can be updated (consumed) only once: the
* notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states
* are all free.
*/
@CordaSerializable
interface ContractState {
/**
* A _participant_ is any party that is able to consume this state in a valid transaction.
*
* The list of participants is required for certain types of transactions. For example, when changing the notary
* for this state, every participant has to be involved and approve the transaction
* so that they receive the updated state, and don't end up in a situation where they can no longer use a state
* they possess, since someone consumed that state during the notary change process.
*
* The participants list should normally be derived from the contents of the state.
*/
val participants: List<AbstractParty>
}

View File

@ -20,106 +20,6 @@ interface NamedByHash {
val id: SecureHash val id: SecureHash
} }
// DOCSTART 1
/**
* A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
* file that the program can use to persist data across transactions. States are immutable: once created they are never
* updated, instead, any changes must generate a new successor state. States can be updated (consumed) only once: the
* notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states
* are all free.
*/
@CordaSerializable
interface ContractState {
/**
* An instance of the contract class that will verify this state.
*
* # Discussion
*
* This field is not the final design, it's just a piece of temporary scaffolding. Once the contract sandbox is
* further along, this field will become a description of which attachments are acceptable for defining the
* contract.
*
* Recall that an attachment is a zip file that can be referenced from any transaction. The contents of the
* attachments are merged together and cannot define any overlapping files, thus for any given transaction there
* is a miniature file system in which each file can be precisely mapped to the defining attachment.
*
* Attachments may contain many things (data files, legal documents, etc) but mostly they contain JVM bytecode.
* The class files inside define not only [Contract] implementations but also the classes that define the states.
* Within the rest of a transaction, user-providable components are referenced by name only.
*
* This means that a smart contract in Corda does two things:
*
* 1. Define the data structures that compose the ledger (the states)
* 2. Define the rules for updating those structures
*
* The first is merely a utility role ... in theory contract code could manually parse byte streams by hand.
* The second is vital to the integrity of the ledger. So this field needs to be able to express constraints like:
*
* - Only attachment 733c350f396a727655be1363c06635ba355036bd54a5ed6e594fd0b5d05f42f6 may be used with this state.
* - Any attachment signed by public key 2d1ce0e330c52b8055258d776c40 may be used with this state.
* - Attachments (1, 2, 3) may all be used with this state.
*
* and so on. In this way it becomes possible for the business logic governing a state to be evolved, if the
* constraints are flexible enough.
*
* Because contract classes often also define utilities that generate relevant transactions, and because attachments
* cannot know their own hashes, we will have to provide various utilities to assist with obtaining the right
* code constraints from within the contract code itself.
*
* TODO: Implement the above description. See COR-226
*/
val contract: Contract
/**
* A _participant_ is any party that is able to consume this state in a valid transaction.
*
* The list of participants is required for certain types of transactions. For example, when changing the notary
* for this state, every participant has to be involved and approve the transaction
* so that they receive the updated state, and don't end up in a situation where they can no longer use a state
* they possess, since someone consumed that state during the notary change process.
*
* The participants list should normally be derived from the contents of the state.
*/
val participants: List<AbstractParty>
}
// DOCEND 1
// DOCSTART 4
/**
* A wrapper for [ContractState] containing additional platform-level state information.
* This is the definitive state that is stored on the ledger and used in transaction outputs.
*/
@CordaSerializable
data class TransactionState<out T : ContractState> @JvmOverloads constructor(
/** The custom contract state */
val data: T,
/** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
val notary: Party,
/**
* All contract states may be _encumbered_ by up to one other state.
*
* The encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks
* that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and
* the contract code and rules of the encumbrance state will also be verified during the execution of the transaction.
* For example, a cash contract state could be encumbered with a time-lock contract state; the cash state is then only
* processable in a transaction that verifies that the time specified in the encumbrance time-lock has passed.
*
* The encumbered state refers to another by index, and the referred encumbrance state
* is an output state in a particular position on the same transaction that created the encumbered state. An alternative
* implementation would be encumbering by reference to a [StateRef], which would allow the specification of encumbrance
* by a state created in a prior transaction.
*
* Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction,
* otherwise the transaction is not valid.
*/
val encumbrance: Int? = null)
// DOCEND 4
/** Wraps the [ContractState] in a [TransactionState] object */
infix fun <T : ContractState> T.`with notary`(newNotary: Party) = withNotary(newNotary)
infix fun <T : ContractState> T.withNotary(newNotary: Party) = TransactionState(this, newNotary)
/** /**
* The [Issued] data class holds the details of an on ledger digital asset. * The [Issued] data class holds the details of an on ledger digital asset.
* In particular it gives the public credentials of the entity that created these digital tokens * In particular it gives the public credentials of the entity that created these digital tokens
@ -250,7 +150,7 @@ data class StateAndRef<out T : ContractState>(val state: TransactionState<T>, va
/** Filters a list of [StateAndRef] objects according to the type of the states */ /** Filters a list of [StateAndRef] objects according to the type of the states */
inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filterStatesOfType(): List<StateAndRef<T>> { inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filterStatesOfType(): List<StateAndRef<T>> {
return mapNotNull { if (it.state.data is T) StateAndRef(TransactionState(it.state.data, it.state.notary), it.ref) else null } return mapNotNull { if (it.state.data is T) StateAndRef(TransactionState(it.state.data, it.state.contract, it.state.notary), it.ref) else null }
} }
/** /**
@ -297,7 +197,7 @@ interface MoveCommand : CommandData {
} }
/** Indicates that this transaction replaces the inputs contract state to another contract state */ /** Indicates that this transaction replaces the inputs contract state to another contract state */
data class UpgradeCommand(val upgradedContractClass: Class<out UpgradedContract<*, *>>) : CommandData data class UpgradeCommand(val upgradedContractClass: ContractClassName) : CommandData
// DOCSTART 6 // DOCSTART 6
/** A [Command] where the signing parties have been looked up if they have a well known/recognised institutional key. */ /** A [Command] where the signing parties have been looked up if they have a well known/recognised institutional key. */
@ -345,7 +245,7 @@ annotation class LegalProseReference(val uri: String)
* @param NewState the upgraded contract state. * @param NewState the upgraded contract state.
*/ */
interface UpgradedContract<in OldState : ContractState, out NewState : ContractState> : Contract { interface UpgradedContract<in OldState : ContractState, out NewState : ContractState> : Contract {
val legacyContract: Class<out Contract> val legacyContract: ContractClassName
/** /**
* Upgrade contract's state object to a new state object. * Upgrade contract's state object to a new state object.
* *
@ -374,3 +274,11 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) {
require(!bytes.all { it == 0.toByte() }) { "Privacy salt should not be all zeros." } require(!bytes.all { it == 0.toByte() }) { "Privacy salt should not be all zeros." }
} }
} }
/**
* A convenience class for passing around a state and it's contract
*
* @property state A state
* @property contract The contract that should verify the state
*/
data class StateAndContract(val state: ContractState, val contract: ContractClassName)

View File

@ -0,0 +1,74 @@
package net.corda.core.contracts
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
typealias ContractClassName = String
/**
* A wrapper for [ContractState] containing additional platform-level state information and contract information.
* This is the definitive state that is stored on the ledger and used in transaction outputs.
*/
@CordaSerializable
data class TransactionState<out T : ContractState> @JvmOverloads constructor(
/** The custom contract state */
val data: T,
/**
* An instance of the contract class that will verify this state.
*
* # Discussion
*
* This field is not the final design, it's just a piece of temporary scaffolding. Once the contract sandbox is
* further along, this field will become a description of which attachments are acceptable for defining the
* contract.
*
* Recall that an attachment is a zip file that can be referenced from any transaction. The contents of the
* attachments are merged together and cannot define any overlapping files, thus for any given transaction there
* is a miniature file system in which each file can be precisely mapped to the defining attachment.
*
* Attachments may contain many things (data files, legal documents, etc) but mostly they contain JVM bytecode.
* The class files inside define not only [Contract] implementations but also the classes that define the states.
* Within the rest of a transaction, user-providable components are referenced by name only.
*
* This means that a smart contract in Corda does two things:
*
* 1. Define the data structures that compose the ledger (the states)
* 2. Define the rules for updating those structures
*
* The first is merely a utility role ... in theory contract code could manually parse byte streams by hand.
* The second is vital to the integrity of the ledger. So this field needs to be able to express constraints like:
*
* - Only attachment 733c350f396a727655be1363c06635ba355036bd54a5ed6e594fd0b5d05f42f6 may be used with this state.
* - Any attachment signed by public key 2d1ce0e330c52b8055258d776c40 may be used with this state.
* - Attachments (1, 2, 3) may all be used with this state.
*
* and so on. In this way it becomes possible for the business logic governing a state to be evolved, if the
* constraints are flexible enough.
*
* Because contract classes often also define utilities that generate relevant transactions, and because attachments
* cannot know their own hashes, we will have to provide various utilities to assist with obtaining the right
* code constraints from within the contract code itself.
*
* TODO: Implement the above description. See COR-226
*/
val contract: ContractClassName,
/** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
val notary: Party,
/**
* All contract states may be _encumbered_ by up to one other state.
*
* The encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks
* that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and
* the contract code and rules of the encumbrance state will also be verified during the execution of the transaction.
* For example, a cash contract state could be encumbered with a time-lock contract state; the cash state is then only
* processable in a transaction that verifies that the time specified in the encumbrance time-lock has passed.
*
* The encumbered state refers to another by index, and the referred encumbrance state
* is an output state in a particular position on the same transaction that created the encumbered state. An alternative
* implementation would be encumbering by reference to a [StateRef], which would allow the specification of encumbrance
* by a state created in a prior transaction.
*
* Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction,
* otherwise the transaction is not valid.
*/
val encumbrance: Int? = null)

View File

@ -16,6 +16,9 @@ sealed class TransactionVerificationException(val txId: SecureHash, message: Str
class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable)
: TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause)
class ContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable)
: TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause)
class MoreThanOneNotary(txId: SecureHash) class MoreThanOneNotary(txId: SecureHash)
: TransactionVerificationException(txId, "More than one notary", null) : TransactionVerificationException(txId, "More than one notary", null)

View File

@ -32,7 +32,7 @@ object ContractUpgradeFlow {
@Suspendable @Suspendable
override fun call(): Void? { override fun call(): Void? {
val upgrade = upgradedContractClass.newInstance() val upgrade = upgradedContractClass.newInstance()
if (upgrade.legacyContract != stateAndRef.state.data.contract.javaClass) { if (upgrade.legacyContract != stateAndRef.state.contract) {
throw FlowException("The contract state cannot be upgraded using provided UpgradedContract.") throw FlowException("The contract state cannot be upgraded using provided UpgradedContract.")
} }
serviceHub.contractUpgradeService.storeAuthorisedContractUpgrade(stateAndRef.ref, upgradedContractClass) serviceHub.contractUpgradeService.storeAuthorisedContractUpgrade(stateAndRef.ref, upgradedContractClass)
@ -73,8 +73,8 @@ object ContractUpgradeFlow {
return TransactionBuilder(stateRef.state.notary) return TransactionBuilder(stateRef.state.notary)
.withItems( .withItems(
stateRef, stateRef,
contractUpgrade.upgrade(stateRef.state.data), StateAndContract(contractUpgrade.upgrade(stateRef.state.data), upgradedContractClass.name),
Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants.map { it.owningKey }), Command(UpgradeCommand(upgradedContractClass.name), stateRef.state.data.participants.map { it.owningKey }),
privacySalt privacySalt
) )
} }
@ -99,23 +99,23 @@ object ContractUpgradeFlow {
@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.inputStates.single(), verify(tx.inputs.single().state,
tx.outputStates.single(), tx.outputs.single(),
tx.commandsOfType<UpgradeCommand>().single()) tx.commandsOfType<UpgradeCommand>().single())
} }
@JvmStatic @JvmStatic
fun verify(input: ContractState, output: ContractState, commandData: Command<UpgradeCommand>) { fun verify(input: TransactionState<ContractState>, output: TransactionState<ContractState>, commandData: Command<UpgradeCommand>) {
val command = commandData.value val command = commandData.value
val participantKeys: Set<PublicKey> = input.participants.map { it.owningKey }.toSet() val participantKeys: Set<PublicKey> = input.data.participants.map { it.owningKey }.toSet()
val keysThatSigned: Set<PublicKey> = commandData.signers.toSet() val keysThatSigned: Set<PublicKey> = commandData.signers.toSet()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *> val upgradedContract = javaClass.classLoader.loadClass(command.upgradedContractClass).newInstance() as UpgradedContract<ContractState, *>
requireThat { requireThat {
"The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys) "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys)
"Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract) "Inputs state reference the legacy contract" using (input.contract == upgradedContract.legacyContract)
"Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass) "Outputs state reference the upgraded contract" using (output.contract == command.upgradedContractClass)
"Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input)) "Output state must be an upgraded version of the input state" using (output.data == upgradedContract.upgrade(input.data))
} }
} }
} }
@ -138,8 +138,8 @@ object ContractUpgradeFlow {
"The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
} }
ContractUpgradeFlow.Acceptor.verify( ContractUpgradeFlow.Acceptor.verify(
oldStateAndRef.state.data, oldStateAndRef.state,
expectedTx.outRef<ContractState>(0).state.data, expectedTx.outRef<ContractState>(0).state,
expectedTx.toLedgerTransaction(serviceHub).commandsOfType<UpgradeCommand>().single()) expectedTx.toLedgerTransaction(serviceHub).commandsOfType<UpgradeCommand>().single())
} }
} }

View File

@ -68,8 +68,14 @@ data class LedgerTransaction(
* If any contract fails to verify, the whole transaction is considered to be invalid. * If any contract fails to verify, the whole transaction is considered to be invalid.
*/ */
private fun verifyContracts() { private fun verifyContracts() {
val contracts = (inputs.map { it.state.data.contract } + outputs.map { it.data.contract }).toSet() val contracts = (inputs.map { it.state.contract } + outputs.map { it.contract }).toSet()
for (contract in contracts) { for (contractClassName in contracts) {
val contract = try {
javaClass.classLoader.loadClass(contractClassName).asSubclass(Contract::class.java).getConstructor().newInstance()
} catch(e: ClassNotFoundException) {
throw TransactionVerificationException.ContractCreationError(id, contractClassName, e)
}
try { try {
contract.verify(this) contract.verify(this)
} catch(e: Throwable) { } catch(e: Throwable) {

View File

@ -7,6 +7,7 @@ import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import java.lang.UnsupportedOperationException
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.time.Duration import java.time.Duration
@ -58,7 +59,8 @@ open class TransactionBuilder(
is StateAndRef<*> -> addInputState(t) is StateAndRef<*> -> addInputState(t)
is SecureHash -> addAttachment(t) is SecureHash -> addAttachment(t)
is TransactionState<*> -> addOutputState(t) is TransactionState<*> -> addOutputState(t)
is ContractState -> addOutputState(t) is StateAndContract -> addOutputState(t.state, t.contract)
is ContractState -> throw UnsupportedOperationException("Removed as of V1: please use a StateAndContract instead")
is Command<*> -> addCommand(t) is Command<*> -> addCommand(t)
is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.") is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.")
is TimeWindow -> setTimeWindow(t) is TimeWindow -> setTimeWindow(t)
@ -99,14 +101,14 @@ open class TransactionBuilder(
} }
@JvmOverloads @JvmOverloads
fun addOutputState(state: ContractState, notary: Party, encumbrance: Int? = null): TransactionBuilder { fun addOutputState(state: ContractState, contract: ContractClassName, notary: Party, encumbrance: Int? = null): TransactionBuilder {
return addOutputState(TransactionState(state, notary, encumbrance)) return addOutputState(TransactionState(state, contract, notary, encumbrance))
} }
/** A default notary must be specified during builder construction to use this method */ /** A default notary must be specified during builder construction to use this method */
fun addOutputState(state: ContractState): TransactionBuilder { fun addOutputState(state: ContractState, contract: ContractClassName): TransactionBuilder {
checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" } checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" }
addOutputState(state, notary!!) addOutputState(state, contract, notary!!)
return this return this
} }

View File

@ -5,6 +5,8 @@ import net.corda.testing.contracts.DummyContractV2
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DUMMY_V2_PROGRAM_ID
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -16,18 +18,18 @@ class DummyContractV2Tests {
@Test @Test
fun `upgrade from v1`() { fun `upgrade from v1`() {
val contractUpgrade = DummyContractV2() val contractUpgrade = DummyContractV2()
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY)
val v1Ref = StateRef(SecureHash.randomSHA256(), 0) val v1Ref = StateRef(SecureHash.randomSHA256(), 0)
val v1StateAndRef = StateAndRef(v1State, v1Ref) val v1StateAndRef = StateAndRef(v1State, v1Ref)
val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef) val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef)
assertEquals(v1Ref, tx.inputs.single()) assertEquals(v1Ref, tx.inputs.single())
val expectedOutput = TransactionState(contractUpgrade.upgrade(v1State.data), DUMMY_NOTARY) val expectedOutput = TransactionState(contractUpgrade.upgrade(v1State.data), DUMMY_V2_PROGRAM_ID, DUMMY_NOTARY)
val actualOutput = tx.outputs.single() val actualOutput = tx.outputs.single()
assertEquals(expectedOutput, actualOutput) assertEquals(expectedOutput, actualOutput)
val actualCommand = tx.commands.map { it.value }.single() val actualCommand = tx.commands.map { it.value }.single()
assertTrue((actualCommand as UpgradeCommand).upgradedContractClass == DummyContractV2::class.java) assertTrue((actualCommand as UpgradeCommand).upgradedContractClass == DummyContractV2::class.java.name)
} }
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
@ -33,12 +34,10 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() {
private class StringTypeDummyState(val data: String) : ContractState { private class StringTypeDummyState(val data: String) : ContractState {
override val contract: Contract = DummyContract()
override val participants: List<AbstractParty> = emptyList() override val participants: List<AbstractParty> = emptyList()
} }
private class IntTypeDummyState(val data: Int) : ContractState { private class IntTypeDummyState(val data: Int) : ContractState {
override val contract: Contract = DummyContract()
override val participants: List<AbstractParty> = emptyList() override val participants: List<AbstractParty> = emptyList()
} }
@ -54,12 +53,12 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() {
val dummyState = makeDummyState(data) val dummyState = makeDummyState(data)
val fakeIssueTx = services.signInitialTransaction( val fakeIssueTx = services.signInitialTransaction(
TransactionBuilder(notary = DUMMY_NOTARY) TransactionBuilder(notary = DUMMY_NOTARY)
.addOutputState(dummyState) .addOutputState(dummyState, DUMMY_PROGRAM_ID)
.addCommand(dummyCommand()) .addCommand(dummyCommand())
) )
services.recordTransactions(fakeIssueTx) services.recordTransactions(fakeIssueTx)
val dummyStateRef = StateRef(fakeIssueTx.id, 0) val dummyStateRef = StateRef(fakeIssueTx.id, 0)
return StateAndRef(TransactionState(dummyState, DUMMY_NOTARY, null), dummyStateRef) return StateAndRef(TransactionState(dummyState, DUMMY_PROGRAM_ID, DUMMY_NOTARY, null), dummyStateRef)
} }
private fun makeDummyTransaction(): LedgerTransaction { private fun makeDummyTransaction(): LedgerTransaction {
@ -67,8 +66,8 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() {
for (i in 0..4) { for (i in 0..4) {
tx.addInputState(makeDummyStateAndRef(i)) tx.addInputState(makeDummyStateAndRef(i))
tx.addInputState(makeDummyStateAndRef(i.toString())) tx.addInputState(makeDummyStateAndRef(i.toString()))
tx.addOutputState(makeDummyState(i)) tx.addOutputState(makeDummyState(i), DUMMY_PROGRAM_ID)
tx.addOutputState(makeDummyState(i.toString())) tx.addOutputState(makeDummyState(i.toString()), DUMMY_PROGRAM_ID)
tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey)) tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey))
tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey)) tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey))
} }

View File

@ -4,6 +4,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.CASH_PROGRAM_ID
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
@ -12,7 +13,7 @@ import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
val TEST_TIMELOCK_ID = TransactionEncumbranceTests.DummyTimeLock() val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock"
class TransactionEncumbranceTests { class TransactionEncumbranceTests {
val defaultIssuer = MEGA_CORP.ref(1) val defaultIssuer = MEGA_CORP.ref(1)
@ -40,7 +41,6 @@ class TransactionEncumbranceTests {
val validFrom: Instant val validFrom: Instant
) : ContractState { ) : ContractState {
override val participants: List<AbstractParty> = emptyList() override val participants: List<AbstractParty> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID
} }
} }
@ -48,9 +48,9 @@ class TransactionEncumbranceTests {
fun `state can be encumbered`() { fun `state can be encumbered`() {
ledger { ledger {
transaction { transaction {
input { state } input(CASH_PROGRAM_ID) { state }
output(encumbrance = 1) { stateWithNewOwner } output(CASH_PROGRAM_ID, encumbrance = 1) { stateWithNewOwner }
output("5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
verifies() verifies()
} }
@ -61,14 +61,14 @@ class TransactionEncumbranceTests {
fun `state can transition if encumbrance rules are met`() { fun `state can transition if encumbrance rules are met`() {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("state encumbered by 5pm time-lock") { state } output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state }
output("5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
} }
// Un-encumber the output if the time of the transaction is later than the timelock. // Un-encumber the output if the time of the transaction is later than the timelock.
transaction { transaction {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output { stateWithNewOwner } output(CASH_PROGRAM_ID) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
verifies() verifies()
@ -80,14 +80,14 @@ class TransactionEncumbranceTests {
fun `state cannot transition if the encumbrance contract fails to verify`() { fun `state cannot transition if the encumbrance contract fails to verify`() {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("state encumbered by 5pm time-lock") { state } output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state }
output("5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
} }
// The time of the transaction is earlier than the time specified in the encumbering timelock. // The time of the transaction is earlier than the time specified in the encumbering timelock.
transaction { transaction {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output { state } output(CASH_PROGRAM_ID) { state }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timeWindow(FOUR_PM) timeWindow(FOUR_PM)
this `fails with` "the time specified in the time-lock has passed" this `fails with` "the time specified in the time-lock has passed"
@ -99,12 +99,12 @@ class TransactionEncumbranceTests {
fun `state must be consumed along with its encumbrance`() { fun `state must be consumed along with its encumbrance`() {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("state encumbered by 5pm time-lock", encumbrance = 1) { state } output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state }
output("5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
} }
transaction { transaction {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
output { stateWithNewOwner } output(CASH_PROGRAM_ID) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"
@ -116,8 +116,8 @@ class TransactionEncumbranceTests {
fun `state cannot be encumbered by itself`() { fun `state cannot be encumbered by itself`() {
ledger { ledger {
transaction { transaction {
input { state } input(CASH_PROGRAM_ID) { state }
output(encumbrance = 0) { stateWithNewOwner } output(CASH_PROGRAM_ID, encumbrance = 0) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 0 in OUTPUT" this `fails with` "Missing required encumbrance 0 in OUTPUT"
} }
@ -128,9 +128,9 @@ class TransactionEncumbranceTests {
fun `encumbrance state index must be valid`() { fun `encumbrance state index must be valid`() {
ledger { ledger {
transaction { transaction {
input { state } input(CASH_PROGRAM_ID) { state }
output(encumbrance = 2) { stateWithNewOwner } output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner }
output { timeLock } output(TEST_TIMELOCK_ID) { timeLock }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 2 in OUTPUT" this `fails with` "Missing required encumbrance 2 in OUTPUT"
} }
@ -141,14 +141,14 @@ class TransactionEncumbranceTests {
fun `correct encumbrance state must be provided`() { fun `correct encumbrance state must be provided`() {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("state encumbered by some other state", encumbrance = 1) { state } output(CASH_PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state }
output("some other state") { state } output(CASH_PROGRAM_ID, "some other state") { state }
output("5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
} }
transaction { transaction {
input("state encumbered by some other state") input("state encumbered by some other state")
input("5pm time-lock") input("5pm time-lock")
output { stateWithNewOwner } output(CASH_PROGRAM_ID) { stateWithNewOwner }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"

View File

@ -8,6 +8,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
@ -93,7 +94,7 @@ class TransactionTests : TestDependencyInjectionBase() {
@Test @Test
fun `transactions with no inputs can have any notary`() { fun `transactions with no inputs can have any notary`() {
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY)
val inputs = emptyList<StateAndRef<*>>() val inputs = emptyList<StateAndRef<*>>()
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
val commands = emptyList<CommandWithParties<CommandData>>() val commands = emptyList<CommandWithParties<CommandData>>()
@ -133,7 +134,7 @@ class TransactionTests : TestDependencyInjectionBase() {
@Test @Test
fun `general transactions cannot change notary`() { fun `general transactions cannot change notary`() {
val notary: Party = DUMMY_NOTARY val notary: Party = DUMMY_NOTARY
val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary) val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, notary)
val outState = inState.copy(notary = ALICE) val outState = inState.copy(notary = ALICE)
val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0))) val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0)))
val outputs = listOf(outState) val outputs = listOf(outState)
@ -158,7 +159,7 @@ class TransactionTests : TestDependencyInjectionBase() {
@Test @Test
fun `transactions with identical contents must have different ids`() { fun `transactions with identical contents must have different ids`() {
val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY)
fun buildTransaction() = WireTransaction( fun buildTransaction() = WireTransaction(
inputs = emptyList(), inputs = emptyList(),
attachments = emptyList(), attachments = emptyList(),

View File

@ -9,6 +9,7 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.CASH_PROGRAM_ID
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.* import net.corda.testing.*
import org.junit.Test import org.junit.Test
@ -31,13 +32,13 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() {
val testLedger = ledger { val testLedger = ledger {
unverifiedTransaction { unverifiedTransaction {
output("MEGA_CORP cash") { output(CASH_PROGRAM_ID, "MEGA_CORP cash") {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP owner = MEGA_CORP
) )
} }
output("dummy cash 1") { output(CASH_PROGRAM_ID, "dummy cash 1") {
Cash.State( Cash.State(
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MINI_CORP owner = MINI_CORP
@ -47,7 +48,7 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() {
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP)) output(CASH_PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()

View File

@ -2,6 +2,7 @@ package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -10,6 +11,7 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.testing.MINI_CORP_KEY import net.corda.testing.MINI_CORP_KEY
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
@ -88,7 +90,7 @@ class CollectSignaturesFlowTests {
val myInputKeys = state.participants.map { it.owningKey } val myInputKeys = state.participants.map { it.owningKey }
val myKeys = myInputKeys + (identities[serviceHub.myInfo.legalIdentity] ?: serviceHub.myInfo.legalIdentity).owningKey val myKeys = myInputKeys + (identities[serviceHub.myInfo.legalIdentity] ?: serviceHub.myInfo.legalIdentity).owningKey
val command = Command(DummyContract.Commands.Create(), myInputKeys) val command = Command(DummyContract.Commands.Create(), myInputKeys)
val builder = TransactionBuilder(notary).withItems(state, command) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command)
val ptx = serviceHub.signInitialTransaction(builder) val ptx = serviceHub.signInitialTransaction(builder)
val stx = subFlow(CollectSignaturesFlow(ptx, myKeys)) val stx = subFlow(CollectSignaturesFlow(ptx, myKeys))
val ftx = subFlow(FinalityFlow(stx)).single() val ftx = subFlow(FinalityFlow(stx)).single()
@ -109,7 +111,7 @@ class CollectSignaturesFlowTests {
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
val myInputKeys = state.participants.map { it.owningKey } val myInputKeys = state.participants.map { it.owningKey }
val command = Command(DummyContract.Commands.Create(), myInputKeys) val command = Command(DummyContract.Commands.Create(), myInputKeys)
val builder = TransactionBuilder(notary).withItems(state, command) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command)
val ptx = serviceHub.signInitialTransaction(builder) val ptx = serviceHub.signInitialTransaction(builder)
val stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys)) val stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys))
val ftx = subFlow(FinalityFlow(stx)).single() val ftx = subFlow(FinalityFlow(stx)).single()

View File

@ -14,6 +14,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.CASH_PROGRAM_ID
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.CordaRPCOpsImpl import net.corda.node.internal.CordaRPCOpsImpl
@ -234,13 +235,14 @@ class ContractUpgradeFlowTest {
assertEquals<Collection<AbstractParty>>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") assertEquals<Collection<AbstractParty>>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
} }
val CASHV2_PROGRAM_ID = "net.corda.core.flows.ContractUpgradeFlowTest.CashV2"
class CashV2 : UpgradedContract<Cash.State, CashV2.State> { class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
override val legacyContract = Cash::class.java override val legacyContract = CASH_PROGRAM_ID
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> { data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
override val owner: AbstractParty = owners.first() override val owner: AbstractParty = owners.first()
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet() override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
override val contract = CashV2()
override val participants = owners override val participants = owners
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))

View File

@ -12,6 +12,7 @@ import kotlin.test.assertFailsWith
class VaultUpdateTests { class VaultUpdateTests {
val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract"
object DummyContract : Contract { object DummyContract : Contract {
@ -20,9 +21,7 @@ class VaultUpdateTests {
} }
private class DummyState : ContractState { private class DummyState : ContractState {
override val participants: List<AbstractParty> override val participants: List<AbstractParty> = emptyList()
get() = emptyList()
override val contract = VaultUpdateTests.DummyContract
} }
private val stateRef0 = StateRef(SecureHash.randomSHA256(), 0) private val stateRef0 = StateRef(SecureHash.randomSHA256(), 0)
@ -31,11 +30,11 @@ class VaultUpdateTests {
private val stateRef3 = StateRef(SecureHash.randomSHA256(), 3) private val stateRef3 = StateRef(SecureHash.randomSHA256(), 3)
private val stateRef4 = StateRef(SecureHash.randomSHA256(), 4) private val stateRef4 = StateRef(SecureHash.randomSHA256(), 4)
private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef0) private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef0)
private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef1) private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef1)
private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef2) private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef2)
private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef3) private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef3)
private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef4) private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef4)
@Test @Test
fun `nothing plus nothing is nothing`() { fun `nothing plus nothing is nothing`() {

View File

@ -15,9 +15,9 @@ import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
val TEST_PROGRAM_ID = TransactionSerializationTests.TestCash()
class TransactionSerializationTests : TestDependencyInjectionBase() { class TransactionSerializationTests : TestDependencyInjectionBase() {
val TEST_CASH_PROGRAM_ID = "net.corda.core.serialization.TransactionSerializationTests.TestCash"
class TestCash : Contract { class TestCash : Contract {
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
} }
@ -26,7 +26,6 @@ class TransactionSerializationTests : TestDependencyInjectionBase() {
val deposit: PartyAndReference, val deposit: PartyAndReference,
val amount: Amount<Currency>, val amount: Amount<Currency>,
override val owner: AbstractParty) : OwnableState { override val owner: AbstractParty) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
@ -42,9 +41,9 @@ class TransactionSerializationTests : TestDependencyInjectionBase() {
// It refers to a fake TX/state that we don't bother creating here. // It refers to a fake TX/state that we don't bother creating here.
val depositRef = MINI_CORP.ref(1) val depositRef = MINI_CORP.ref(1)
val fakeStateRef = generateStateRef() val fakeStateRef = generateStateRef()
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef) val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY), fakeStateRef)
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY) val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY) val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY)
val megaCorpServices = MockServices(MEGA_CORP_KEY) val megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY) val notaryServices = MockServices(DUMMY_NOTARY_KEY)

View File

@ -6,6 +6,9 @@ from the previous milestone release.
UNRELEASED UNRELEASED
---------- ----------
* ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to
support dynamic classloading of contract and contract constraints.
* About half of the code in test-utils has been moved to a new module ``node-driver``, * About half of the code in test-utils has been moved to a new module ``node-driver``,
and the test scope modules are now located in a ``testing`` directory. and the test scope modules are now located in a ``testing`` directory.

View File

@ -22,6 +22,7 @@ import net.corda.core.utilities.UntrustworthyData;
import net.corda.core.utilities.X500NameUtils; import net.corda.core.utilities.X500NameUtils;
import net.corda.finance.contracts.asset.Cash; import net.corda.finance.contracts.asset.Cash;
import net.corda.testing.contracts.DummyContract; import net.corda.testing.contracts.DummyContract;
import net.corda.testing.contracts.DummyContractKt;
import net.corda.testing.contracts.DummyState; import net.corda.testing.contracts.DummyState;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -331,7 +332,7 @@ public class FlowCookbookJava {
// We can also add items using methods for the individual components: // We can also add items using methods for the individual components:
// DOCSTART 28 // DOCSTART 28
txBuilder.addInputState(ourStateAndRef); txBuilder.addInputState(ourStateAndRef);
txBuilder.addOutputState(ourOutput); txBuilder.addOutputState(ourOutput, DummyContractKt.getDUMMY_PROGRAM_ID());
txBuilder.addCommand(ourCommand); txBuilder.addCommand(ourCommand);
txBuilder.addAttachment(ourAttachment); txBuilder.addAttachment(ourAttachment);
// DOCEND 28 // DOCEND 28

View File

@ -20,6 +20,7 @@ import net.corda.core.utilities.*
import net.corda.core.utilities.ProgressTracker.Step import net.corda.core.utilities.ProgressTracker.Step
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.ALICE_PUBKEY import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import java.security.PublicKey import java.security.PublicKey
@ -309,7 +310,7 @@ object FlowCookbook {
// We can also add items using methods for the individual components: // We can also add items using methods for the individual components:
// DOCSTART 28 // DOCSTART 28
txBuilder.addInputState(ourStateAndRef) txBuilder.addInputState(ourStateAndRef)
txBuilder.addOutputState(ourOutput) txBuilder.addOutputState(ourOutput, DUMMY_PROGRAM_ID)
txBuilder.addCommand(ourCommand) txBuilder.addCommand(ourCommand)
txBuilder.addAttachment(ourAttachment) txBuilder.addAttachment(ourAttachment)
// DOCEND 28 // DOCEND 28

View File

@ -1,10 +1,7 @@
package net.corda.docs package net.corda.docs
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Amount import net.corda.core.contracts.*
import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.withoutIssuer
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.* import net.corda.core.flows.*
@ -17,6 +14,7 @@ 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.unwrap import net.corda.core.utilities.unwrap
import net.corda.finance.contracts.asset.CASH_PROGRAM_ID
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CashSchemaV1
import java.util.* import java.util.*
@ -184,8 +182,8 @@ class ForeignExchangeFlow(val tradeId: String,
// Build and add the inputs and outputs // Build and add the inputs and outputs
builder.withItems(*ourInputStates.toTypedArray()) builder.withItems(*ourInputStates.toTypedArray())
builder.withItems(*theirInputStates.toTypedArray()) builder.withItems(*theirInputStates.toTypedArray())
builder.withItems(*ourOutputState.toTypedArray()) builder.withItems(*ourOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray())
builder.withItems(*theirOutputState.toTypedArray()) builder.withItems(*theirOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray())
// We have already validated their response and trust our own data // We have already validated their response and trust our own data
// so we can sign. Note the returned SignedTransaction is still not fully signed // so we can sign. Note the returned SignedTransaction is still not fully signed

View File

@ -26,6 +26,8 @@ enum class WorkflowState {
REJECTED REJECTED
} }
val TRADE_APPROVAL_PROGRAM_ID = "net.corda.docs.TradeApprovalContract"
/** /**
* Minimal contract to encode a simple workflow with one initial state and two possible eventual states. * Minimal contract to encode a simple workflow with one initial state and two possible eventual states.
* It is assumed one party unilaterally submits and the other manually retrieves the deal and completes it. * It is assumed one party unilaterally submits and the other manually retrieves the deal and completes it.
@ -44,9 +46,7 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract {
val source: Party, val source: Party,
val counterparty: Party, val counterparty: Party,
val state: WorkflowState = WorkflowState.NEW, val state: WorkflowState = WorkflowState.NEW,
override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId), override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId)) : LinearState {
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
val parties: List<Party> get() = listOf(source, counterparty) val parties: List<Party> get() = listOf(source, counterparty)
override val participants: List<AbstractParty> get() = parties override val participants: List<AbstractParty> get() = parties
} }
@ -108,7 +108,7 @@ class SubmitTradeApprovalFlow(val tradeId: String,
val notary = serviceHub.networkMapCache.getAnyNotary() val notary = serviceHub.networkMapCache.getAnyNotary()
// Create the TransactionBuilder and populate with the new state. // Create the TransactionBuilder and populate with the new state.
val tx = TransactionBuilder(notary) val tx = TransactionBuilder(notary)
.withItems(tradeProposal, Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) .withItems(StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey)))
tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds)
// We can automatically sign as there is no untrusted data. // We can automatically sign as there is no untrusted data.
val signedTx = serviceHub.signInitialTransaction(tx) val signedTx = serviceHub.signInitialTransaction(tx)
@ -168,7 +168,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow
val tx = TransactionBuilder(notary). val tx = TransactionBuilder(notary).
withItems( withItems(
latestRecord, latestRecord,
newState, StateAndContract(newState, TRADE_APPROVAL_PROGRAM_ID),
Command(TradeApprovalContract.Commands.Completed(), Command(TradeApprovalContract.Commands.Completed(),
listOf(serviceHub.myInfo.legalIdentity.owningKey, latestRecord.state.data.source.owningKey))) listOf(serviceHub.myInfo.legalIdentity.owningKey, latestRecord.state.data.source.owningKey)))
tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds)

View File

@ -74,7 +74,7 @@ define an ``IOUState``:
class IOUState(val value: Int, class IOUState(val value: Int,
val lender: Party, val lender: Party,
val borrower: Party) : ContractState { val borrower: Party) : ContractState {
override val contract = TemplateContract() override val contract = "net.corda.contract.TemplateContract"
override val participants get() = listOf(lender, borrower) override val participants get() = listOf(lender, borrower)
} }

View File

@ -102,7 +102,7 @@ A state is a class that stores data that is checked by the contract. A commercia
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState { ) : OwnableState {
override val contract = CommercialPaper() override val contract = "net.corda.finance.contracts.CommercialPaper"
override val participants = listOf(owner) override val participants = listOf(owner)
fun withoutOwner() = copy(owner = AnonymousParty(NullPublicKey)) fun withoutOwner() = copy(owner = AnonymousParty(NullPublicKey))

View File

@ -10,13 +10,11 @@ import net.corda.finance.contracts.FixOf
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
val UNIVERSAL_PROGRAM_ID = UniversalContract() val UNIVERSAL_PROGRAM_ID = "net.corda.finance.contracts.universal.UniversalContract"
class UniversalContract : Contract { class UniversalContract : Contract {
data class State(override val participants: List<AbstractParty>, data class State(override val participants: List<AbstractParty>,
val details: Arrangement) : ContractState { val details: Arrangement) : ContractState
override val contract = UNIVERSAL_PROGRAM_ID
}
interface Commands : CommandData { interface Commands : CommandData {
@ -317,7 +315,7 @@ class UniversalContract : Contract {
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
tx.addOutputState(State(listOf(notary), arrangement)) tx.addOutputState(State(listOf(notary), arrangement), UNIVERSAL_PROGRAM_ID)
tx.addCommand(Commands.Issue(), at.party.owningKey) tx.addCommand(Commands.Issue(), at.party.owningKey)
} }
} }

View File

@ -166,7 +166,7 @@ class Cap {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output { stateInitial } output(UNIVERSAL_PROGRAM_ID) { stateInitial }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -183,8 +183,8 @@ class Cap {
@Test @Test
fun `first fixing`() { fun `first fixing`() {
transaction { transaction {
input { stateInitial } input(UNIVERSAL_PROGRAM_ID) { stateInitial }
output { stateAfterFixingFirst } output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -228,9 +228,9 @@ class Cap {
@Test @Test
fun `first execute`() { fun `first execute`() {
transaction { transaction {
input { stateAfterFixingFirst } input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
output { stateAfterExecutionFirst } output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
output { statePaymentFirst } output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
@ -248,8 +248,8 @@ class Cap {
@Test @Test
fun `final execute`() { fun `final execute`() {
transaction { transaction {
input { stateAfterFixingFinal } input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal }
output { statePaymentFinal } output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
@ -267,8 +267,8 @@ class Cap {
@Test @Test
fun `second fixing`() { fun `second fixing`() {
transaction { transaction {
input { stateAfterExecutionFirst } input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
output { stateAfterFixingFinal } output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {

View File

@ -53,7 +53,7 @@ class Caplet {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output { stateStart } output(UNIVERSAL_PROGRAM_ID) { stateStart }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -70,8 +70,8 @@ class Caplet {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input { stateFixed } input(UNIVERSAL_PROGRAM_ID) { stateFixed }
output { stateFinal } output(UNIVERSAL_PROGRAM_ID) { stateFinal }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -88,8 +88,8 @@ class Caplet {
@Test @Test
fun `fixing`() { fun `fixing`() {
transaction { transaction {
input { stateStart } input(UNIVERSAL_PROGRAM_ID) { stateStart }
output { stateFixed } output(UNIVERSAL_PROGRAM_ID) { stateFixed }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {

View File

@ -50,7 +50,7 @@ class FXFwdTimeOption
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output { inState } output(UNIVERSAL_PROGRAM_ID) { inState }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -71,9 +71,9 @@ class FXFwdTimeOption
@Test @Test
fun `maturity, bank exercise`() { fun `maturity, bank exercise`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_AFTER_MATURITY) timeWindow(TEST_TX_TIME_AFTER_MATURITY)
@ -103,9 +103,9 @@ class FXFwdTimeOption
@Test @Test
fun `maturity, corp exercise`() { fun `maturity, corp exercise`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_BEFORE_MATURITY) timeWindow(TEST_TX_TIME_BEFORE_MATURITY)

View File

@ -42,7 +42,7 @@ class FXSwap {
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output { inState } output(UNIVERSAL_PROGRAM_ID) { inState }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -63,9 +63,9 @@ class FXSwap {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -82,9 +82,9 @@ class FXSwap {
@Test @Test
fun `execute - reversed order`() { fun `execute - reversed order`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -101,9 +101,9 @@ class FXSwap {
@Test @Test
fun `execute - not authorized`() { fun `execute - not authorized`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
@ -114,9 +114,9 @@ class FXSwap {
@Test @Test
fun `execute - before maturity`() { fun `execute - before maturity`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_TOO_EARLY) timeWindow(TEST_TX_TIME_TOO_EARLY)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
@ -127,8 +127,8 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 1`() { fun `execute - outState mismatch 1`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
@ -139,9 +139,9 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 2`() { fun `execute - outState mismatch 2`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outStateBad2 } output(UNIVERSAL_PROGRAM_ID) { outStateBad2 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
@ -152,9 +152,9 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 3`() { fun `execute - outState mismatch 3`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outStateBad1 } output(UNIVERSAL_PROGRAM_ID) { outStateBad1 }
output { outState2 } output(UNIVERSAL_PROGRAM_ID) { outState2 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
@ -165,9 +165,9 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 4`() { fun `execute - outState mismatch 4`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState1 } output(UNIVERSAL_PROGRAM_ID) { outState1 }
output { outStateBad3 } output(UNIVERSAL_PROGRAM_ID) { outStateBad3 }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }

View File

@ -133,7 +133,7 @@ class IRS {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output { stateInitial } output(UNIVERSAL_PROGRAM_ID) { stateInitial }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -150,8 +150,8 @@ class IRS {
@Test @Test
fun `first fixing`() { fun `first fixing`() {
transaction { transaction {
input { stateInitial } input(UNIVERSAL_PROGRAM_ID) { stateInitial }
output { stateAfterFixingFirst } output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -195,9 +195,9 @@ class IRS {
@Test @Test
fun `first execute`() { fun `first execute`() {
transaction { transaction {
input { stateAfterFixingFirst } input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
output { stateAfterExecutionFirst } output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
output { statePaymentFirst } output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)

View File

@ -142,7 +142,7 @@ class RollOutTests {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output { stateStart } output(UNIVERSAL_PROGRAM_ID) { stateStart }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -159,9 +159,9 @@ class RollOutTests {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input { stateStart } input(UNIVERSAL_PROGRAM_ID) { stateStart }
output { stateStep1a } output(UNIVERSAL_PROGRAM_ID) { stateStep1a }
output { stateStep1b } output(UNIVERSAL_PROGRAM_ID) { stateStep1b }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
/* tweak { /* tweak {

View File

@ -59,7 +59,7 @@ class Swaption {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output { stateInitial } output(UNIVERSAL_PROGRAM_ID) { stateInitial }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {

View File

@ -49,7 +49,7 @@ class ZeroCouponBond {
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output { inState } output(UNIVERSAL_PROGRAM_ID) { inState }
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
@ -65,8 +65,8 @@ class ZeroCouponBond {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState } output(UNIVERSAL_PROGRAM_ID) { outState }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
@ -83,8 +83,8 @@ class ZeroCouponBond {
@Test @Test
fun `execute - not authorized`() { fun `execute - not authorized`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outState } output(UNIVERSAL_PROGRAM_ID) { outState }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
@ -95,8 +95,8 @@ class ZeroCouponBond {
@Test @Test
fun `execute - outState mismatch`() { fun `execute - outState mismatch`() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
output { outStateWrong } output(UNIVERSAL_PROGRAM_ID) { outStateWrong }
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
@ -107,10 +107,10 @@ class ZeroCouponBond {
@Test @Test
fun move() { fun move() {
transaction { transaction {
input { inState } input(UNIVERSAL_PROGRAM_ID) { inState }
tweak { tweak {
output { outStateMove } output(UNIVERSAL_PROGRAM_ID) { outStateMove }
command(acmeCorp.owningKey) { command(acmeCorp.owningKey) {
UniversalContract.Commands.Move(acmeCorp, momAndPop) UniversalContract.Commands.Move(acmeCorp, momAndPop)
} }
@ -118,14 +118,14 @@ class ZeroCouponBond {
} }
tweak { tweak {
output { inState } output(UNIVERSAL_PROGRAM_ID) { inState }
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
UniversalContract.Commands.Move(acmeCorp, momAndPop) UniversalContract.Commands.Move(acmeCorp, momAndPop)
} }
this `fails with` "output state does not reflect move command" this `fails with` "output state does not reflect move command"
} }
output { outStateMove } output(UNIVERSAL_PROGRAM_ID) { outStateMove }
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
UniversalContract.Commands.Move(acmeCorp, momAndPop) UniversalContract.Commands.Move(acmeCorp, momAndPop)

View File

@ -7,11 +7,12 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.nodeapi.DummyContractBackdoor import net.corda.nodeapi.DummyContractBackdoor
val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract() val ANOTHER_DUMMY_PROGRAM_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract"
class AnotherDummyContract : Contract, DummyContractBackdoor { class AnotherDummyContract : Contract, DummyContractBackdoor {
val magicString = "helloworld"
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ANOTHER_DUMMY_PROGRAM_ID
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
get() = emptyList() get() = emptyList()
} }
@ -26,7 +27,7 @@ class AnotherDummyContract : Contract, DummyContractBackdoor {
override fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { override fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
val state = State(magicNumber) val state = State(magicNumber)
return TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) return TransactionBuilder(notary).withItems(StateAndContract(state, ANOTHER_DUMMY_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey))
} }
override fun inspectState(state: ContractState): Int = (state as State).magicNumber override fun inspectState(state: ContractState): Int = (state as State).magicNumber

View File

@ -31,7 +31,7 @@ import static net.corda.core.contracts.ContractsDSL.requireThat;
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class JavaCommercialPaper implements Contract { public class JavaCommercialPaper implements Contract {
private static final Contract JCP_PROGRAM_ID = new JavaCommercialPaper(); static final String JCP_PROGRAM_ID = "net.corda.finance.contracts.JavaCommercialPaper";
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class State implements OwnableState, ICommercialPaperState { public static class State implements OwnableState, ICommercialPaperState {
@ -90,12 +90,6 @@ public class JavaCommercialPaper implements Contract {
return maturityDate; return maturityDate;
} }
@NotNull
@Override
public Contract getContract() {
return JCP_PROGRAM_ID;
}
@Override @Override
public boolean equals(Object that) { public boolean equals(Object that) {
if (this == that) return true; if (this == that) return true;
@ -236,7 +230,7 @@ public class JavaCommercialPaper implements Contract {
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) { public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) {
State state = new State(issuance, issuance.getParty(), faceValue, maturityDate); State state = new State(issuance, issuance.getParty(), faceValue, maturityDate);
TransactionState output = new TransactionState<>(state, notary, encumbrance); TransactionState output = new TransactionState<>(state, JCP_PROGRAM_ID, notary, encumbrance);
return new TransactionBuilder(notary).withItems(output, new Command<>(new Commands.Issue(), issuance.getParty().getOwningKey())); return new TransactionBuilder(notary).withItems(output, new Command<>(new Commands.Issue(), issuance.getParty().getOwningKey()));
} }
@ -253,7 +247,7 @@ public class JavaCommercialPaper implements Contract {
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, AbstractParty newOwner) { public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, AbstractParty newOwner) {
tx.addInputState(paper); tx.addInputState(paper);
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance())); tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), JCP_PROGRAM_ID, paper.getState().getNotary(), paper.getState().getEncumbrance()));
tx.addCommand(new Command<>(new Commands.Move(), paper.getState().getData().getOwner().getOwningKey())); tx.addCommand(new Command<>(new Commands.Move(), paper.getState().getData().getOwner().getOwningKey()));
} }

View File

@ -43,7 +43,7 @@ import java.util.*
// TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance. // TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance.
class CommercialPaper : Contract { class CommercialPaper : Contract {
companion object { companion object {
val CP_PROGRAM_ID = CommercialPaper() val CP_PROGRAM_ID = "net.corda.finance.contracts.CommercialPaper"
} }
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
@ -51,7 +51,6 @@ class CommercialPaper : Contract {
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState { ) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(Commands.Move(), copy(owner = newOwner))
@ -87,7 +86,6 @@ class CommercialPaper : Contract {
} }
/** @suppress */ infix fun `owned by`(owner: AbstractParty) = copy(owner = owner) /** @suppress */ infix fun `owned by`(owner: AbstractParty) = copy(owner = owner)
/** @suppress */ infix fun `with notary`(notary: Party) = TransactionState(this, notary)
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -164,7 +162,7 @@ class CommercialPaper : Contract {
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant,
notary: Party): TransactionBuilder { notary: Party): TransactionBuilder {
val state = State(issuance, issuance.party, faceValue, maturityDate) val state = State(issuance, issuance.party, faceValue, maturityDate)
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) return TransactionBuilder(notary = notary).withItems(StateAndContract(state, CP_PROGRAM_ID), Command(Commands.Issue(), issuance.party.owningKey))
} }
/** /**
@ -172,7 +170,7 @@ class CommercialPaper : Contract {
*/ */
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
tx.addInputState(paper) tx.addInputState(paper)
tx.addOutputState(paper.state.data.withOwner(newOwner)) tx.addOutputState(paper.state.data.withOwner(newOwner), CP_PROGRAM_ID)
tx.addCommand(Commands.Move(), paper.state.data.owner.owningKey) tx.addCommand(Commands.Move(), paper.state.data.owner.owningKey)
} }

View File

@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicReference
// //
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
val CASH_PROGRAM_ID = Cash() val CASH_PROGRAM_ID = "net.corda.finance.contracts.asset.Cash"
/** /**
* Pluggable interface to allow for different cash selection provider implementations * Pluggable interface to allow for different cash selection provider implementations
@ -123,7 +123,6 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey) override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey)
override val contract = CASH_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency> override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency>
@ -191,7 +190,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: AbstractParty, notary: Party) fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: AbstractParty, notary: Party)
= generateIssue(tx, TransactionState(State(amount, owner), notary), Commands.Issue()) = generateIssue(tx, TransactionState(State(amount, owner), CASH_PROGRAM_ID, notary), Commands.Issue())
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: AbstractParty) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: AbstractParty)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))

View File

@ -33,7 +33,7 @@ import java.util.*
class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, CommodityContract.State>() { class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, CommodityContract.State>() {
companion object { companion object {
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
val COMMODITY_PROGRAM_ID = CommodityContract() val COMMODITY_PROGRAM_ID = "net.corda.finance.contracts.asset.CommodityContract"
} }
/** A state representing a commodity claim against some party */ /** A state representing a commodity claim against some party */
@ -45,8 +45,6 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
) : FungibleAsset<Commodity> { ) : FungibleAsset<Commodity> {
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: AbstractParty) constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: AbstractParty)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val contract = COMMODITY_PROGRAM_ID
override val exitKeys: Set<PublicKey> = Collections.singleton(owner.owningKey) override val exitKeys: Set<PublicKey> = Collections.singleton(owner.owningKey)
override val participants = listOf(owner) override val participants = listOf(owner)
@ -160,7 +158,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: AbstractParty, notary: Party) fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: AbstractParty, notary: Party)
= generateIssue(tx, TransactionState(State(amount, owner), notary), Commands.Issue()) = generateIssue(tx, TransactionState(State(amount, owner), COMMODITY_PROGRAM_ID, notary), Commands.Issue())
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: AbstractParty) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: AbstractParty)

View File

@ -64,7 +64,7 @@ data class MultilateralNetState<P : Any>(
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
val OBLIGATION_PROGRAM_ID = Obligation<Currency>() val OBLIGATION_PROGRAM_ID = "net.corda.finance.contracts.asset.Obligation"
/** /**
* An obligation contract commits the obligor to delivering a specified amount of a fungible asset (for example the * An obligation contract commits the obligor to delivering a specified amount of a fungible asset (for example the
@ -134,7 +134,6 @@ class Obligation<P : Any> : Contract {
val beneficiary: AbstractParty val beneficiary: AbstractParty
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> { ) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template)) override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
override val contract = OBLIGATION_PROGRAM_ID
override val exitKeys: Collection<PublicKey> = setOf(beneficiary.owningKey) override val exitKeys: Collection<PublicKey> = setOf(beneficiary.owningKey)
val dueBefore: Instant = template.dueBefore val dueBefore: Instant = template.dueBefore
override val participants: List<AbstractParty> = listOf(obligor, beneficiary) override val participants: List<AbstractParty> = listOf(obligor, beneficiary)
@ -491,7 +490,7 @@ class Obligation<P : Any> : Contract {
tx.withItems(*inputs) tx.withItems(*inputs)
val out = states.reduce(State<P>::net) val out = states.reduce(State<P>::net)
if (out.quantity > 0L) if (out.quantity > 0L)
tx.addOutputState(out) tx.addOutputState(out, OBLIGATION_PROGRAM_ID)
tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey) tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey)
} }
@ -534,7 +533,7 @@ class Obligation<P : Any> : Contract {
beneficiary: AbstractParty, beneficiary: AbstractParty,
notary: Party) { notary: Party) {
val issuanceDef = Terms(NonEmptySet.of(acceptableContract), NonEmptySet.of(amount.token), dueBefore) val issuanceDef = Terms(NonEmptySet.of(acceptableContract), NonEmptySet.of(amount.token), dueBefore)
OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, amount.quantity, beneficiary), notary), Commands.Issue()) OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, amount.quantity, beneficiary), OBLIGATION_PROGRAM_ID, notary), Commands.Issue())
} }
/** /**
@ -554,7 +553,7 @@ class Obligation<P : Any> : Contract {
pennies: Long, pennies: Long,
beneficiary: AbstractParty, beneficiary: AbstractParty,
notary: Party) notary: Party)
= OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), notary), Commands.Issue()) = OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), OBLIGATION_PROGRAM_ID, notary), Commands.Issue())
fun generatePaymentNetting(tx: TransactionBuilder, fun generatePaymentNetting(tx: TransactionBuilder,
issued: Issued<Obligation.Terms<P>>, issued: Issued<Obligation.Terms<P>>,
@ -587,7 +586,7 @@ class Obligation<P : Any> : Contract {
netState.template, entry.value.quantity, entry.key.second) netState.template, entry.value.quantity, entry.key.second)
} }
// Add the new states to the TX // Add the new states to the TX
.forEach { tx.addOutputState(it, notary) } .forEach { tx.addOutputState(it, OBLIGATION_PROGRAM_ID, notary) }
tx.addCommand(Commands.Net(NetType.PAYMENT), signers.map { it.owningKey }) tx.addCommand(Commands.Net(NetType.PAYMENT), signers.map { it.owningKey })
} }
@ -618,7 +617,7 @@ class Obligation<P : Any> : Contract {
stateAndRefs.forEach { stateAndRef -> stateAndRefs.forEach { stateAndRef ->
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
tx.addInputState(stateAndRef) tx.addInputState(stateAndRef)
tx.addOutputState(outState, notary) tx.addOutputState(outState, OBLIGATION_PROGRAM_ID, notary)
partiesUsed.add(stateAndRef.state.data.beneficiary) partiesUsed.add(stateAndRef.state.data.beneficiary)
} }
tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct()) tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct())
@ -671,13 +670,13 @@ class Obligation<P : Any> : Contract {
val assetState = ref.state.data val assetState = ref.state.data
val amount = Amount(assetState.amount.quantity, assetState.amount.token.product) val amount = Amount(assetState.amount.quantity, assetState.amount.token.product)
if (obligationRemaining >= amount) { if (obligationRemaining >= amount) {
tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), notary) tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), OBLIGATION_PROGRAM_ID, notary)
obligationRemaining -= amount obligationRemaining -= amount
} else { } else {
val change = Amount(obligationRemaining.quantity, assetState.amount.token) val change = Amount(obligationRemaining.quantity, assetState.amount.token)
// Split the state in two, sending the change back to the previous beneficiary // Split the state in two, sending the change back to the previous beneficiary
tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), notary) tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), OBLIGATION_PROGRAM_ID, notary)
tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), notary) tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), OBLIGATION_PROGRAM_ID, notary)
obligationRemaining -= Amount(0L, obligationRemaining.token) obligationRemaining -= Amount(0L, obligationRemaining.token)
} }
assetSigners.add(assetState.owner) assetSigners.add(assetState.owner)
@ -686,7 +685,7 @@ class Obligation<P : Any> : Contract {
// If we haven't cleared the full obligation, add the remainder as an output // If we haven't cleared the full obligation, add the remainder as an output
if (obligationRemaining.quantity > 0L) { if (obligationRemaining.quantity > 0L) {
tx.addOutputState(State(Lifecycle.NORMAL, obligationIssuer, template, obligationRemaining.quantity, obligationOwner), notary) tx.addOutputState(State(Lifecycle.NORMAL, obligationIssuer, template, obligationRemaining.quantity, obligationOwner), OBLIGATION_PROGRAM_ID, notary)
} else { } else {
// Destroy all of the states // Destroy all of the states
} }

View File

@ -224,7 +224,7 @@ object TwoPartyTradeFlow {
tx.addInputState(assetForSale) tx.addInputState(assetForSale)
val (command, state) = assetForSale.state.data.withNewOwner(buyerAnonymousIdentity.party) val (command, state) = assetForSale.state.data.withNewOwner(buyerAnonymousIdentity.party)
tx.addOutputState(state, assetForSale.state.notary) tx.addOutputState(state, assetForSale.state.contract, assetForSale.state.notary)
tx.addCommand(command, assetForSale.state.data.owner.owningKey) tx.addCommand(command, assetForSale.state.data.owner.owningKey)
// We set the transaction's time-window: it may be that none of the contracts need this! // We set the transaction's time-window: it may be that none of the contracts need this!

View File

@ -19,41 +19,42 @@ public class CashTestsJava {
private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef); private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef);
private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getMEGA_CORP_PUBKEY())); private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getMEGA_CORP_PUBKEY()));
private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY())); private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY()));
private final String CASH_PROGRAM_ID = CashUtilities.getCASH_PROGRAM_ID();
@Test @Test
public void trivial() { public void trivial() {
transaction(tx -> { transaction(tx -> {
tx.input(inState); tx.input(CASH_PROGRAM_ID, inState);
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY()))); tw.output(CASH_PROGRAM_ID, new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY())));
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("the amounts balance"); return tw.failsWith("the amounts balance");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(outState); tw.output(CASH_PROGRAM_ID, outState);
tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE); tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE);
// Invalid command // Invalid command
return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command"); return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(outState); tw.output(CASH_PROGRAM_ID, outState);
tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("the owning keys are a subset of the signing keys"); return tw.failsWith("the owning keys are a subset of the signing keys");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(outState); tw.output(CASH_PROGRAM_ID, outState);
// issuedBy() can't be directly imported because it conflicts with other identically named functions // issuedBy() can't be directly imported because it conflicts with other identically named functions
// with different overloads (for some reason). // with different overloads (for some reason).
tw.output(outState.issuedBy(getMINI_CORP())); tw.output(CASH_PROGRAM_ID, outState.issuedBy(getMINI_CORP()));
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("at least one cash input"); return tw.failsWith("at least one cash input");
}); });
// Simple reallocation works. // Simple reallocation works.
return tx.tweak(tw -> { return tx.tweak(tw -> {
tw.output(outState); tw.output(CASH_PROGRAM_ID, outState);
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.verifies(); return tw.verifies();
}); });

View File

@ -31,6 +31,7 @@ interface ICommercialPaperTestTemplate {
fun getIssueCommand(notary: Party): CommandData fun getIssueCommand(notary: Party): CommandData
fun getRedeemCommand(notary: Party): CommandData fun getRedeemCommand(notary: Party): CommandData
fun getMoveCommand(): CommandData fun getMoveCommand(): CommandData
fun getContract(): ContractClassName
} }
class JavaCommercialPaperTest : ICommercialPaperTestTemplate { class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
@ -44,6 +45,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getIssueCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Issue() override fun getIssueCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Issue()
override fun getRedeemCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Redeem() override fun getRedeemCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Redeem()
override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move() override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move()
override fun getContract() = JavaCommercialPaper.JCP_PROGRAM_ID
} }
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
@ -57,6 +59,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue()
override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem()
override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move()
override fun getContract() = CommercialPaper.CP_PROGRAM_ID
} }
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
@ -70,6 +73,7 @@ class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue()
override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem()
override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move()
override fun getContract() = CommercialPaper.CP_PROGRAM_ID
} }
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
@ -89,13 +93,13 @@ class CommercialPaperTestsGeneric {
val someProfits = 1200.DOLLARS `issued by` issuer val someProfits = 1200.DOLLARS `issued by` issuer
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE) output(CASH_PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE)
output("some profits", someProfits.STATE `owned by` MEGA_CORP) output(CASH_PROGRAM_ID, "some profits", someProfits.STATE `owned by` MEGA_CORP)
} }
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") { transaction("Issuance") {
output("paper") { thisTest.getPaper() } output(thisTest.getContract(), "paper") { thisTest.getPaper() }
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -106,8 +110,8 @@ class CommercialPaperTestsGeneric {
transaction("Trade") { transaction("Trade") {
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP } output(CASH_PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP }
output("alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) } output(thisTest.getContract(), "alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
this.verifies() this.verifies()
@ -120,8 +124,8 @@ class CommercialPaperTestsGeneric {
input("some profits") input("some profits")
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) { fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE } output(CASH_PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE `owned by` ALICE }
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP } output(CASH_PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP }
} }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
@ -142,7 +146,7 @@ class CommercialPaperTestsGeneric {
timeWindow(TEST_TX_TIME + 8.days) timeWindow(TEST_TX_TIME + 8.days)
tweak { tweak {
output { "paper".output<ICommercialPaperState>() } output(thisTest.getContract()) { "paper".output<ICommercialPaperState>() }
this `fails with` "must be destroyed" this `fails with` "must be destroyed"
} }
@ -154,7 +158,7 @@ class CommercialPaperTestsGeneric {
@Test @Test
fun `key mismatch at issue`() { fun `key mismatch at issue`() {
transaction { transaction {
output { thisTest.getPaper() } output(thisTest.getContract()) { thisTest.getPaper() }
command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
@ -164,7 +168,7 @@ class CommercialPaperTestsGeneric {
@Test @Test
fun `face value is not zero`() { fun `face value is not zero`() {
transaction { transaction {
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
@ -174,7 +178,7 @@ class CommercialPaperTestsGeneric {
@Test @Test
fun `maturity date not in the past`() { fun `maturity date not in the past`() {
transaction { transaction {
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "maturity date is not in the past" this `fails with` "maturity date is not in the past"
@ -184,8 +188,8 @@ class CommercialPaperTestsGeneric {
@Test @Test
fun `issue cannot replace an existing state`() { fun `issue cannot replace an existing state`() {
transaction { transaction {
input(thisTest.getPaper()) input(thisTest.getContract(), thisTest.getPaper())
output { thisTest.getPaper() } output(thisTest.getContract()) { thisTest.getPaper() }
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"

View File

@ -84,33 +84,33 @@ class CashTests : TestDependencyInjectionBase() {
@Test @Test
fun trivial() { fun trivial() {
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
tweak { tweak {
output { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
output { outState } output(CASH_PROGRAM_ID) { outState }
command(ALICE_PUBKEY) { DummyCommandData } command(ALICE_PUBKEY) { DummyCommandData }
// Invalid command // Invalid command
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
} }
tweak { tweak {
output { outState } output(CASH_PROGRAM_ID) { outState }
command(BOB_PUBKEY) { Cash.Commands.Move() } command(BOB_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the owning keys are a subset of the signing keys" this `fails with` "the owning keys are a subset of the signing keys"
} }
tweak { tweak {
output { outState } output(CASH_PROGRAM_ID) { outState }
output { outState `issued by` MINI_CORP } output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "at least one cash input" this `fails with` "at least one cash input"
} }
// Simple reallocation works. // Simple reallocation works.
tweak { tweak {
output { outState } output(CASH_PROGRAM_ID) { outState }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -121,8 +121,8 @@ class CashTests : TestDependencyInjectionBase() {
fun `issue by move`() { fun `issue by move`() {
// Check we can't "move" money into existence. // Check we can't "move" money into existence.
transaction { transaction {
input { DummyState() } input(CASH_PROGRAM_ID) { DummyState() }
output { outState } output(CASH_PROGRAM_ID) { outState }
command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } command(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
this `fails with` "there is at least one cash input for this group" this `fails with` "there is at least one cash input for this group"
@ -134,12 +134,12 @@ class CashTests : TestDependencyInjectionBase() {
// Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised // Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised
// institution is allowed to issue as much cash as they want. // institution is allowed to issue as much cash as they want.
transaction { transaction {
output { outState } output(CASH_PROGRAM_ID) { outState }
command(ALICE_PUBKEY) { Cash.Commands.Issue() } command(ALICE_PUBKEY) { Cash.Commands.Issue() }
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
transaction { transaction {
output { output(CASH_PROGRAM_ID) {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
owner = AnonymousParty(ALICE_PUBKEY) owner = AnonymousParty(ALICE_PUBKEY)
@ -182,8 +182,8 @@ class CashTests : TestDependencyInjectionBase() {
fun `extended issue examples`() { fun `extended issue examples`() {
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
transaction { transaction {
input { issuerInState } input(CASH_PROGRAM_ID) { issuerInState }
output { inState.copy(amount = inState.amount * 2) } output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) }
// Move fails: not allowed to summon money. // Move fails: not allowed to summon money.
tweak { tweak {
@ -200,24 +200,24 @@ class CashTests : TestDependencyInjectionBase() {
// Can't use an issue command to lower the amount. // Can't use an issue command to lower the amount.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { inState.copy(amount = inState.amount.splitEvenly(2).first()) } output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have an issue command that doesn't actually issue money. // Can't have an issue command that doesn't actually issue money.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { inState } output(CASH_PROGRAM_ID) { inState }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have any other commands if we have an issue command (because the issue command overrules them) // Can't have any other commands if we have an issue command (because the issue command overrules them)
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { inState.copy(amount = inState.amount * 2) } output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
@ -252,24 +252,24 @@ class CashTests : TestDependencyInjectionBase() {
transaction { transaction {
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
tweak { tweak {
input { inState } input(CASH_PROGRAM_ID) { inState }
val splits4 = inState.amount.splitEvenly(4) val splits4 = inState.amount.splitEvenly(4)
for (i in 0..3) output { inState.copy(amount = splits4[i]) } for (i in 0..3) output(CASH_PROGRAM_ID) { inState.copy(amount = splits4[i]) }
this.verifies() this.verifies()
} }
// Merging 4 inputs into 2 outputs works. // Merging 4 inputs into 2 outputs works.
tweak { tweak {
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
val splits4 = inState.amount.splitEvenly(4) val splits4 = inState.amount.splitEvenly(4)
for (i in 0..3) input { inState.copy(amount = splits4[i]) } for (i in 0..3) input(CASH_PROGRAM_ID) { inState.copy(amount = splits4[i]) }
for (i in 0..1) output { inState.copy(amount = splits2[i]) } for (i in 0..1) output(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) }
this.verifies() this.verifies()
} }
// Merging 2 inputs into 1 works. // Merging 2 inputs into 1 works.
tweak { tweak {
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
for (i in 0..1) input { inState.copy(amount = splits2[i]) } for (i in 0..1) input(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) }
output { inState } output(CASH_PROGRAM_ID) { inState }
this.verifies() this.verifies()
} }
} }
@ -278,15 +278,15 @@ class CashTests : TestDependencyInjectionBase() {
@Test @Test
fun zeroSizedValues() { fun zeroSizedValues() {
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
input { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } input(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "zero sized inputs" this `fails with` "zero sized inputs"
} }
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { inState } output(CASH_PROGRAM_ID) { inState }
output { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "zero sized outputs" this `fails with` "zero sized outputs"
} }
@ -296,52 +296,52 @@ class CashTests : TestDependencyInjectionBase() {
fun trivialMismatches() { fun trivialMismatches() {
// Can't change issuer. // Can't change issuer.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { outState `issued by` MINI_CORP } output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't change deposit reference when splitting. // Can't change deposit reference when splitting.
transaction { transaction {
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
input { inState } input(CASH_PROGRAM_ID) { inState }
for (i in 0..1) output { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } for (i in 0..1) output(CASH_PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't mix currencies. // Can't mix currencies.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) }
output { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
input { input(CASH_PROGRAM_ID) {
inState.copy( inState.copy(
amount = 150.POUNDS `issued by` defaultIssuer, amount = 150.POUNDS `issued by` defaultIssuer,
owner = AnonymousParty(BOB_PUBKEY) owner = AnonymousParty(BOB_PUBKEY)
) )
} }
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't have superfluous input states from different issuers. // Can't have superfluous input states from different issuers.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
input { inState `issued by` MINI_CORP } input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP }
output { outState } output(CASH_PROGRAM_ID) { outState }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't combine two different deposits at the same issuer. // Can't combine two different deposits at the same issuer.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
input { inState.editDepositRef(3) } input(CASH_PROGRAM_ID) { inState.editDepositRef(3) }
output { outState.copy(amount = inState.amount * 2).editDepositRef(3) } output(CASH_PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "for reference [01]" this `fails with` "for reference [01]"
} }
@ -351,8 +351,8 @@ class CashTests : TestDependencyInjectionBase() {
fun exitLedger() { fun exitLedger() {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
input { issuerInState } input(CASH_PROGRAM_ID) { issuerInState }
output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) }
@ -376,11 +376,11 @@ class CashTests : TestDependencyInjectionBase() {
fun `exit ledger with multiple issuers`() { fun `exit ledger with multiple issuers`() {
// Multi-issuer case. // Multi-issuer case.
transaction { transaction {
input { issuerInState } input(CASH_PROGRAM_ID) { issuerInState }
input { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP } input(CASH_PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP }
output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP } output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP }
output { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output(CASH_PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
@ -398,8 +398,8 @@ class CashTests : TestDependencyInjectionBase() {
fun `exit cash not held by its issuer`() { fun `exit cash not held by its issuer`() {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
input { inState } input(CASH_PROGRAM_ID) { inState }
output { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output(CASH_PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
@ -410,25 +410,25 @@ class CashTests : TestDependencyInjectionBase() {
fun multiIssuer() { fun multiIssuer() {
transaction { transaction {
// Gather 2000 dollars from two different issuers. // Gather 2000 dollars from two different issuers.
input { inState } input(CASH_PROGRAM_ID) { inState }
input { inState `issued by` MINI_CORP } input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) }
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) }
output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP }
this.verifies() this.verifies()
} }
} }
@ -438,10 +438,10 @@ class CashTests : TestDependencyInjectionBase() {
// Check we can do an atomic currency trade tx. // Check we can do an atomic currency trade tx.
transaction { transaction {
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY))
input { inState `owned by` AnonymousParty(ALICE_PUBKEY) } input(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(ALICE_PUBKEY) }
input { pounds } input(CASH_PROGRAM_ID) { pounds }
output { inState `owned by` AnonymousParty(BOB_PUBKEY) } output(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) }
output { pounds `owned by` AnonymousParty(ALICE_PUBKEY) } output(CASH_PROGRAM_ID) { pounds `owned by` AnonymousParty(ALICE_PUBKEY) }
command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
@ -460,7 +460,7 @@ class CashTests : TestDependencyInjectionBase() {
fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) = fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) =
StateAndRef( StateAndRef(
Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1) `with notary` DUMMY_NOTARY, TransactionState<Cash.State>(Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1), CASH_PROGRAM_ID, DUMMY_NOTARY),
StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
) )
@ -753,7 +753,7 @@ class CashTests : TestDependencyInjectionBase() {
fun chainCashDoubleSpendFailsWith() { fun chainCashDoubleSpendFailsWith() {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("MEGA_CORP cash") { output(CASH_PROGRAM_ID, "MEGA_CORP cash") {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP owner = MEGA_CORP
@ -763,7 +763,7 @@ class CashTests : TestDependencyInjectionBase() {
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(ALICE_PUBKEY))) output(CASH_PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(ALICE_PUBKEY)))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -772,7 +772,7 @@ class CashTests : TestDependencyInjectionBase() {
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
// We send it to another pubkey so that the transaction is not identical to the previous one // We send it to another pubkey so that the transaction is not identical to the previous one
output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE)) output(CASH_PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }

View File

@ -30,7 +30,6 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey) override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey)
override val contract = CASH_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency> override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency>

View File

@ -16,6 +16,7 @@ import net.corda.finance.contracts.Commodity
import net.corda.finance.contracts.NetType import net.corda.finance.contracts.NetType
import net.corda.finance.contracts.asset.Obligation.Lifecycle import net.corda.finance.contracts.asset.Obligation.Lifecycle
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import org.junit.After import org.junit.After
@ -54,10 +55,10 @@ class ObligationTests {
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
) = group.apply { ) = group.apply {
unverifiedTransaction { unverifiedTransaction {
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) output(OBLIGATION_PROGRAM_ID, "Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE))
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB)) output(OBLIGATION_PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB))
output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) output(OBLIGATION_PROGRAM_ID, "Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
} }
} }
@ -69,33 +70,33 @@ class ObligationTests {
@Test @Test
fun trivial() { fun trivial() {
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
tweak { tweak {
output { outState.copy(quantity = 2000.DOLLARS.quantity) } output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(CHARLIE.owningKey) { DummyCommandData } command(CHARLIE.owningKey) { DummyCommandData }
// Invalid command // Invalid command
this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command"
} }
tweak { tweak {
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(BOB_PUBKEY) { Obligation.Commands.Move() } command(BOB_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "the owning keys are a subset of the signing keys" this `fails with` "the owning keys are a subset of the signing keys"
} }
tweak { tweak {
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
output { outState `issued by` MINI_CORP } output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "at least one obligation input" this `fails with` "at least one obligation input"
} }
// Simple reallocation works. // Simple reallocation works.
tweak { tweak {
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
@ -106,8 +107,8 @@ class ObligationTests {
fun `issue debt`() { fun `issue debt`() {
// Check we can't "move" debt into existence. // Check we can't "move" debt into existence.
transaction { transaction {
input { DummyState() } input(DUMMY_PROGRAM_ID) { DummyState() }
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "at least one obligation input" this `fails with` "at least one obligation input"
@ -116,12 +117,12 @@ class ObligationTests {
// Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised // Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised
// institution is allowed to issue as much cash as they want. // institution is allowed to issue as much cash as they want.
transaction { transaction {
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(CHARLIE.owningKey) { Obligation.Commands.Issue() } command(CHARLIE.owningKey) { Obligation.Commands.Issue() }
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
transaction { transaction {
output { output(OBLIGATION_PROGRAM_ID) {
Obligation.State( Obligation.State(
obligor = MINI_CORP, obligor = MINI_CORP,
quantity = 1000.DOLLARS.quantity, quantity = 1000.DOLLARS.quantity,
@ -153,8 +154,8 @@ class ObligationTests {
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { inState.copy(quantity = inState.amount.quantity * 2) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) }
// Move fails: not allowed to summon money. // Move fails: not allowed to summon money.
tweak { tweak {
@ -171,24 +172,24 @@ class ObligationTests {
// Can't use an issue command to lower the amount. // Can't use an issue command to lower the amount.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { inState.copy(quantity = inState.amount.quantity / 2) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) }
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have an issue command that doesn't actually issue money. // Can't have an issue command that doesn't actually issue money.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { inState } output(OBLIGATION_PROGRAM_ID) { inState }
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
this `fails with` "" this `fails with` ""
} }
// Can't have any other commands if we have an issue command (because the issue command overrules them). // Can't have any other commands if we have an issue command (because the issue command overrules them).
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { inState.copy(quantity = inState.amount.quantity * 2) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) }
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
@ -223,8 +224,8 @@ class ObligationTests {
@Test @Test
fun `generate close-out net transaction`() { fun `generate close-out net transaction`() {
initialiseTestSerialization() initialiseTestSerialization()
val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID)
val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID)
val tx = TransactionBuilder(DUMMY_NOTARY).apply { val tx = TransactionBuilder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
}.toWireTransaction() }.toWireTransaction()
@ -235,8 +236,8 @@ class ObligationTests {
@Test @Test
fun `generate close-out net transaction with remainder`() { fun `generate close-out net transaction with remainder`() {
initialiseTestSerialization() initialiseTestSerialization()
val obligationAliceToBob = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)) val obligationAliceToBob = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID)
val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID)
val tx = TransactionBuilder(DUMMY_NOTARY).apply { val tx = TransactionBuilder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
}.toWireTransaction() }.toWireTransaction()
@ -250,8 +251,8 @@ class ObligationTests {
@Test @Test
fun `generate payment net transaction`() { fun `generate payment net transaction`() {
initialiseTestSerialization() initialiseTestSerialization()
val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID)
val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID)
val tx = TransactionBuilder(DUMMY_NOTARY).apply { val tx = TransactionBuilder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.state.data.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.state.data.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
}.toWireTransaction() }.toWireTransaction()
@ -262,9 +263,9 @@ class ObligationTests {
@Test @Test
fun `generate payment net transaction with remainder`() { fun `generate payment net transaction with remainder`() {
initialiseTestSerialization() initialiseTestSerialization()
val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID)
val obligationAliceToBobState = obligationAliceToBob.state.data val obligationAliceToBobState = obligationAliceToBob.state.data
val obligationBobToAlice = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)) val obligationBobToAlice = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID)
val obligationBobToAliceState = obligationBobToAlice.state.data val obligationBobToAliceState = obligationBobToAlice.state.data
val tx = TransactionBuilder(DUMMY_NOTARY).apply { val tx = TransactionBuilder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBobState.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBobState.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
@ -275,8 +276,8 @@ class ObligationTests {
assertEquals(expected, actual) assertEquals(expected, actual)
} }
private inline fun <reified T: ContractState> getStateAndRef(state: T): StateAndRef<T> { private inline fun <reified T: ContractState> getStateAndRef(state: T, contractClassName: ContractClassName): StateAndRef<T> {
val txState = TransactionState(state, DUMMY_NOTARY) val txState = TransactionState(state, contractClassName, DUMMY_NOTARY)
return StateAndRef(txState, StateRef(SecureHash.randomSHA256(), 0)) return StateAndRef(txState, StateRef(SecureHash.randomSHA256(), 0))
} }
@ -366,7 +367,7 @@ class ObligationTests {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) } output(OBLIGATION_PROGRAM_ID, "change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) }
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -380,7 +381,7 @@ class ObligationTests {
transaction("Issuance") { transaction("Issuance") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) } output(OBLIGATION_PROGRAM_ID, "change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) }
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "amounts owed on input and output must match" this `fails with` "amounts owed on input and output must match"
@ -434,7 +435,7 @@ class ObligationTests {
transaction("Issuance") { transaction("Issuance") {
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } output(OBLIGATION_PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) }
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -464,7 +465,7 @@ class ObligationTests {
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) }
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
@ -476,10 +477,10 @@ class ObligationTests {
val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) input(CASH_PROGRAM_ID, 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) } output(OBLIGATION_PROGRAM_ID, "Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) }
output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } output(OBLIGATION_PROGRAM_ID, "Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) }
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
@ -491,9 +492,9 @@ class ObligationTests {
val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED)
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob input(OBLIGATION_PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) input(CASH_PROGRAM_ID, 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) }
this `fails with` "all inputs are in the normal state" this `fails with` "all inputs are in the normal state"
@ -506,7 +507,7 @@ class ObligationTests {
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) }
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
@ -526,13 +527,13 @@ class ObligationTests {
// Try settling a simple commodity obligation // Try settling a simple commodity obligation
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB)) output(OBLIGATION_PROGRAM_ID, "Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB))
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE)) output(OBLIGATION_PROGRAM_ID, "Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE))
} }
transaction("Settlement") { transaction("Settlement") {
input("Alice's 1 FCOJ obligation to Bob") input("Alice's 1 FCOJ obligation to Bob")
input("Alice's 1 FCOJ") input("Alice's 1 FCOJ")
output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) } output(OBLIGATION_PROGRAM_ID, "Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) }
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) }
attachment(attachment(commodityContractBytes.inputStream())) attachment(attachment(commodityContractBytes.inputStream()))
@ -548,7 +549,7 @@ class ObligationTests {
cashObligationTestRoots(this) cashObligationTestRoots(this)
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) } output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
this `fails with` "there is a time-window from the authority" this `fails with` "there is a time-window from the authority"
} }
@ -558,8 +559,8 @@ class ObligationTests {
val pastTestTime = TEST_TX_TIME - 7.days val pastTestTime = TEST_TX_TIME - 7.days
val futureTestTime = TEST_TX_TIME + 7.days val futureTestTime = TEST_TX_TIME + 7.days
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime)
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "the due date has passed" this `fails with` "the due date has passed"
@ -568,8 +569,8 @@ class ObligationTests {
// Try defaulting an obligation that is now in the past // Try defaulting an obligation that is now in the past
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime)
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -584,22 +585,22 @@ class ObligationTests {
transaction { transaction {
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
tweak { tweak {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } } repeat(4) { output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } }
this.verifies() this.verifies()
} }
// Merging 4 inputs into 2 outputs works. // Merging 4 inputs into 2 outputs works.
tweak { tweak {
repeat(4) { input { inState.copy(quantity = inState.quantity / 4) } } repeat(4) { input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } }
output { inState.copy(quantity = inState.quantity / 2) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
output { inState.copy(quantity = inState.quantity / 2) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
this.verifies() this.verifies()
} }
// Merging 2 inputs into 1 works. // Merging 2 inputs into 1 works.
tweak { tweak {
input { inState.copy(quantity = inState.quantity / 2) } input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
input { inState.copy(quantity = inState.quantity / 2) } input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
output { inState } output(OBLIGATION_PROGRAM_ID) { inState }
this.verifies() this.verifies()
} }
} }
@ -610,15 +611,15 @@ class ObligationTests {
transaction { transaction {
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
tweak { tweak {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
input { inState.copy(quantity = 0L) } input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) }
this `fails with` "zero sized inputs" this `fails with` "zero sized inputs"
} }
tweak { tweak {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { inState } output(OBLIGATION_PROGRAM_ID) { inState }
output { inState.copy(quantity = 0L) } output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) }
this `fails with` "zero sized outputs" this `fails with` "zero sized outputs"
} }
@ -629,37 +630,37 @@ class ObligationTests {
fun trivialMismatches() { fun trivialMismatches() {
// Can't change issuer. // Can't change issuer.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { outState `issued by` MINI_CORP } output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP }
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't mix currencies. // Can't mix currencies.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) }
output { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) }
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
input { input(OBLIGATION_PROGRAM_ID) {
inState.copy( inState.copy(
quantity = 15000, quantity = 15000,
template = megaCorpPoundSettlement, template = megaCorpPoundSettlement,
beneficiary = AnonymousParty(BOB_PUBKEY) beneficiary = AnonymousParty(BOB_PUBKEY)
) )
} }
output { outState.copy(quantity = 115000) } output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 115000) }
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't have superfluous input states from different issuers. // Can't have superfluous input states from different issuers.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
input { inState `issued by` MINI_CORP } input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP }
output { outState } output(OBLIGATION_PROGRAM_ID) { outState }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
@ -669,8 +670,8 @@ class ObligationTests {
fun `exit single product obligation`() { fun `exit single product obligation`() {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
tweak { tweak {
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
@ -695,11 +696,11 @@ class ObligationTests {
fun `exit multiple product obligations`() { fun `exit multiple product obligations`() {
// Multi-product case. // Multi-product case.
transaction { transaction {
input { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) } input(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) }
input { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) } input(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) }
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } output(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) }
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) } output(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
@ -717,26 +718,26 @@ class ObligationTests {
fun multiIssuer() { fun multiIssuer() {
transaction { transaction {
// Gather 2000 dollars from two different issuers. // Gather 2000 dollars from two different issuers.
input { inState } input(OBLIGATION_PROGRAM_ID) { inState }
input { inState `issued by` MINI_CORP } input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP }
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) } output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) }
output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) }
output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP }
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
@ -747,10 +748,10 @@ class ObligationTests {
// Check we can do an atomic currency trade tx. // Check we can do an atomic currency trade tx.
transaction { transaction {
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY)) val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY))
input { inState `owned by` CHARLIE } input(OBLIGATION_PROGRAM_ID) { inState `owned by` CHARLIE }
input { pounds } input(OBLIGATION_PROGRAM_ID) { pounds }
output { inState `owned by` AnonymousParty(BOB_PUBKEY) } output(OBLIGATION_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) }
output { pounds `owned by` CHARLIE } output(OBLIGATION_PROGRAM_ID) { pounds `owned by` CHARLIE }
command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() } command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() }
this.verifies() this.verifies()

View File

@ -41,11 +41,11 @@ interface DummyContractBackdoor {
fun inspectState(state: ContractState): Int fun inspectState(state: ContractState): Int
} }
val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContract()
class AttachmentClassLoaderTests : TestDependencyInjectionBase() { class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
companion object { companion object {
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
private val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.AttachmentClassLoaderTests.AttachmentDummyContract"
private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext { private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext {
val serviceHub = mock<ServiceHub>() val serviceHub = mock<ServiceHub>()
@ -56,7 +56,6 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
class AttachmentDummyContract : Contract { class AttachmentDummyContract : Contract {
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ATTACHMENT_TEST_PROGRAM_ID
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
get() = listOf() get() = listOf()
} }
@ -71,7 +70,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
val state = State(magicNumber) val state = State(magicNumber)
return TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) return TransactionBuilder(notary).withItems(StateAndContract(state, ATTACHMENT_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey))
} }
} }
@ -96,10 +95,10 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
fun `dynamically load AnotherDummyContract from isolated contracts jar`() { fun `dynamically load AnotherDummyContract from isolated contracts jar`() {
val child = ClassLoaderForTests() val child = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
val contract = contractClass.newInstance() as Contract val contract = contractClass.newInstance() as Contract
assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField<Any?>("legalContractReference").value) assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
} }
fun fakeAttachment(filepath: String, content: String): ByteArray { fun fakeAttachment(filepath: String, content: String): ByteArray {
@ -187,10 +186,10 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")))
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)
val contract = contractClass.newInstance() as Contract val contract = contractClass.newInstance() as Contract
assertEquals(cl, contract.javaClass.classLoader) assertEquals(cl, contract.javaClass.classLoader)
assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField<Any?>("legalContractReference").value) assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
} }
@ -204,7 +203,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
fun createContract2Cash(): Contract { fun createContract2Cash(): Contract {
val cl = ClassLoaderForTests() val cl = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)
return contractClass.newInstance() as Contract return contractClass.newInstance() as Contract
} }
@ -250,7 +249,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl)) val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl))
val state2 = bytes.deserialize(context = context) val state2 = bytes.deserialize(context = context)
assertEquals(cl, state2.contract.javaClass.classLoader) assertEquals(cl, state2.contract.javaClass.classLoader)
@ -259,7 +258,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
// We should be able to load same class from a different class loader and have them be distinct. // We should be able to load same class from a different class loader and have them be distinct.
val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
val context3 = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl2).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl2)) val context3 = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl2).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl2))
val state3 = bytes.deserialize(context = context3) val state3 = bytes.deserialize(context = context3)
assertEquals(cl2, state3.contract.javaClass.classLoader) assertEquals(cl2, state3.contract.javaClass.classLoader)
@ -295,7 +294,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
@Test @Test
fun `test serialization of WireTransaction with statically loaded contract`() { fun `test serialization of WireTransaction with statically loaded contract`() {
val tx = ATTACHMENT_TEST_PROGRAM_ID.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val tx = AttachmentDummyContract().generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val wireTransaction = tx.toWireTransaction() val wireTransaction = tx.toWireTransaction()
val bytes = wireTransaction.serialize() val bytes = wireTransaction.serialize()
val copiedWireTransaction = bytes.deserialize() val copiedWireTransaction = bytes.deserialize()
@ -307,13 +306,13 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
@Test @Test
fun `test serialization of WireTransaction with dynamically loaded contract`() { fun `test serialization of WireTransaction with dynamically loaded contract`() {
val child = ClassLoaderForTests() val child = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
val contract = contractClass.newInstance() as DummyContractBackdoor val contract = contractClass.newInstance() as DummyContractBackdoor
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass) val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass)
.withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child)) .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$State", true, child))
.withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child)) .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$Commands\$Create", true, child))
.withAttachmentStorage(storage) .withAttachmentStorage(storage)
// todo - think about better way to push attachmentStorage down to serializer // todo - think about better way to push attachmentStorage down to serializer
@ -325,14 +324,17 @@ 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.getOutput(0).contract as DummyContractBackdoor // Contracts need to be loaded by the same classloader as the ContractState itself
val contractClassloader = copiedWireTransaction.getOutput(0).javaClass.classLoader
val contract2 = contractClassloader.loadClass(copiedWireTransaction.outputs.first().contract).newInstance() as DummyContractBackdoor
assertEquals(contract2.javaClass.classLoader, copiedWireTransaction.outputs[0].data.javaClass.classLoader)
assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data)) assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
} }
@Test @Test
fun `test deserialize of WireTransaction where contract cannot be found`() = kryoSpecific<AttachmentClassLoaderTests>("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { fun `test deserialize of WireTransaction where contract cannot be found`() = kryoSpecific<AttachmentClassLoaderTests>("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") {
val child = ClassLoaderForTests() val child = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
val contract = contractClass.newInstance() as DummyContractBackdoor val contract = contractClass.newInstance() as DummyContractBackdoor
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
@ -363,7 +365,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
@Test @Test
fun `test loading a class from attachment during deserialization`() { fun `test loading a class from attachment during deserialization`() {
val child = ClassLoaderForTests() val child = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
val contract = contractClass.newInstance() as DummyContractBackdoor val contract = contractClass.newInstance() as DummyContractBackdoor
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val attachmentRef = importJar(storage) val attachmentRef = importJar(storage)
@ -380,7 +382,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
@Test @Test
fun `test loading a class with attachment missing during deserialization`() { fun `test loading a class with attachment missing during deserialization`() {
val child = ClassLoaderForTests() val child = ClassLoaderForTests()
val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child)
val contract = contractClass.newInstance() as DummyContractBackdoor val contract = contractClass.newInstance() as DummyContractBackdoor
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val attachmentRef = SecureHash.randomSHA256() val attachmentRef = SecureHash.randomSHA256()
@ -396,4 +398,4 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() {
} }
assertEquals(attachmentRef, e.ids.single()) assertEquals(attachmentRef, e.ids.single())
} }
} }

View File

@ -163,7 +163,7 @@ class CordaClassResolverTests {
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val attachmentHash = importJar(storage) val attachmentHash = importJar(storage)
val classLoader = AttachmentsClassLoader(arrayOf(attachmentHash).map { storage.openAttachment(it)!! }) val classLoader = AttachmentsClassLoader(arrayOf(attachmentHash).map { storage.openAttachment(it)!! })
val attachedClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, classLoader) val attachedClass = Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract", true, classLoader)
CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass) CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass)
} }

View File

@ -531,16 +531,14 @@ class SerializationOutputTests {
} }
} }
val FOO_PROGRAM_ID = "net.corda.nodeapi.internal.serialization.amqp.SerializationOutputTests.FooContract"
class FooState : ContractState { class FooState : ContractState {
override val contract: Contract override val participants: List<AbstractParty> = emptyList()
get() = FooContract
override val participants: List<AbstractParty>
get() = emptyList()
} }
@Test @Test
fun `test transaction state`() { fun `test transaction state`() {
val state = TransactionState(FooState(), MEGA_CORP) val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP)
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
AbstractAMQPSerializationScheme.registerCustomSerializers(factory) AbstractAMQPSerializationScheme.registerCustomSerializers(factory)

View File

@ -23,6 +23,7 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService
import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
@ -72,7 +73,7 @@ class BFTNotaryServiceTests {
val notary = bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. val notary = bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race.
val f = node.run { val f = node.run {
val trivialTx = signInitialTransaction(notary) { val trivialTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID)
} }
// Create a new consensus while the redundant replica is sleeping: // Create a new consensus while the redundant replica is sleeping:
services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture
@ -96,7 +97,7 @@ class BFTNotaryServiceTests {
val notary = bftNotaryCluster(clusterSize) val notary = bftNotaryCluster(clusterSize)
node.run { node.run {
val issueTx = signInitialTransaction(notary) { val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID)
} }
database.transaction { database.transaction {
services.recordTransactions(issueTx) services.recordTransactions(issueTx)

View File

@ -13,6 +13,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.getX500Name import net.corda.core.utilities.getX500Name
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
@ -45,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity) val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity)
addOutputState(dummyState) addOutputState(dummyState, DUMMY_PROGRAM_ID)
addCommand(dummyCommand(bankA.services.legalIdentityKey)) addCommand(dummyCommand(bankA.services.legalIdentityKey))
this this
} }

View File

@ -10,6 +10,7 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.aliceBobAndNotary import net.corda.testing.aliceBobAndNotary
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
@ -26,7 +27,7 @@ class LargeTransactionsTest {
@Suspendable @Suspendable
override fun call() { override fun call() {
val tx = TransactionBuilder(notary = DUMMY_NOTARY) val tx = TransactionBuilder(notary = DUMMY_NOTARY)
.addOutputState(DummyState()) .addOutputState(DummyState(), DUMMY_PROGRAM_ID)
.addCommand(dummyCommand(serviceHub.legalIdentityKey)) .addCommand(dummyCommand(serviceHub.legalIdentityKey))
.addAttachment(hash1) .addAttachment(hash1)
.addAttachment(hash2) .addAttachment(hash2)

View File

@ -1,13 +1,7 @@
package net.corda.test.node package net.corda.test.node
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command import net.corda.core.contracts.*
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat
import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
@ -71,7 +65,6 @@ fun isQuasarAgentSpecified(): Boolean {
data class Message(val value: String) data class Message(val value: String)
data class MessageState(val message: Message, val by: Party, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState { data class MessageState(val message: Message, val by: Party, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState {
override val contract = MessageContract()
override val participants: List<AbstractParty> = listOf(by) override val participants: List<AbstractParty> = listOf(by)
override fun generateMappedObject(schema: MappedSchema): PersistentState { override fun generateMappedObject(schema: MappedSchema): PersistentState {
@ -104,6 +97,8 @@ object MessageSchemaV1 : MappedSchema(
) : PersistentState() ) : PersistentState()
} }
val MESSAGE_CONTRACT_PROGRAM_ID = "net.corda.test.node.MessageContract"
open class MessageContract : Contract { open class MessageContract : Contract {
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands.Send>() val command = tx.commands.requireSingleCommand<Commands.Send>()
@ -146,7 +141,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransactio
val messageState = MessageState(message = message, by = serviceHub.myInfo.legalIdentity) val messageState = MessageState(message = message, by = serviceHub.myInfo.legalIdentity)
val txCommand = Command(MessageContract.Commands.Send(), messageState.participants.map { it.owningKey }) val txCommand = Command(MessageContract.Commands.Send(), messageState.participants.map { it.owningKey })
val txBuilder = TransactionBuilder(notary).withItems(messageState, txCommand) val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CONTRACT_PROGRAM_ID), txCommand)
progressTracker.currentStep = VERIFYING_TRANSACTION progressTracker.currentStep = VERIFYING_TRANSACTION
txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify() txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()

View File

@ -48,4 +48,4 @@ class ContractUpgradeServiceImpl : ContractUpgradeService {
override fun removeAuthorisedContractUpgrade(ref: StateRef) { override fun removeAuthorisedContractUpgrade(ref: StateRef) {
authorisedUpgrade.remove(ref.toString()) authorisedUpgrade.remove(ref.toString())
} }
} }

View File

@ -32,10 +32,7 @@ import net.corda.core.utilities.unwrap
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.CommercialPaper
import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.*
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.`issued by`
import net.corda.finance.contracts.asset.`owned by`
import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Buyer
import net.corda.finance.flows.TwoPartyTradeFlow.Seller import net.corda.finance.flows.TwoPartyTradeFlow.Seller
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
@ -690,8 +687,8 @@ class TwoPartyTradeFlowTests {
// wants to sell to Bob. // wants to sell to Bob.
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
// Issued money to itself. // Issued money to itself.
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } output(CASH_PROGRAM_ID, "elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } output(CASH_PROGRAM_ID, "elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
if (!withError) { if (!withError) {
command(issuer.party.owningKey) { Cash.Commands.Issue() } command(issuer.party.owningKey) { Cash.Commands.Issue() }
} else { } else {
@ -709,15 +706,15 @@ class TwoPartyTradeFlowTests {
// Bob gets some cash onto the ledger from BoE // Bob gets some cash onto the ledger from BoE
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 1") input("elbonian money 1")
output("bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner } output(CASH_PROGRAM_ID, "bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
command(interimOwner.owningKey) { Cash.Commands.Move() } command(interimOwner.owningKey) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 2") input("elbonian money 2")
output("bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner } output(CASH_PROGRAM_ID, "bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } // Change output. output(CASH_PROGRAM_ID, notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } // Change output.
command(interimOwner.owningKey) { Cash.Commands.Move() } command(interimOwner.owningKey) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -734,7 +731,7 @@ class TwoPartyTradeFlowTests {
attachmentID: SecureHash?, attachmentID: SecureHash?,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
output("alice's paper", notary = notary) { output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary) {
CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days) CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)
} }
command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() } command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() }

View File

@ -15,6 +15,7 @@ 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.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.getTestPartyAndCertificate
@ -142,9 +143,9 @@ class NotaryChangeTests {
val tx = TransactionBuilder(null).apply { val tx = TransactionBuilder(null).apply {
addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey)) addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey))
addOutputState(stateA, notary, encumbrance = 2) // Encumbered by stateB addOutputState(stateA, DUMMY_PROGRAM_ID, notary, encumbrance = 2) // Encumbered by stateB
addOutputState(stateC, notary) addOutputState(stateC, DUMMY_PROGRAM_ID, notary)
addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC addOutputState(stateB, DUMMY_PROGRAM_ID, notary, encumbrance = 1) // Encumbered by stateC
} }
val stx = node.services.signInitialTransaction(tx) val stx = node.services.signInitialTransaction(tx)
node.services.recordTransactions(stx) node.services.recordTransactions(stx)
@ -170,7 +171,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> { fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
val state = TransactionState(DummyContract.MultiOwnerState(0, val state = TransactionState(DummyContract.MultiOwnerState(0,
listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity) listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity)
val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand()) val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand())
val signedByA = nodeA.services.signInitialTransaction(tx) val signedByA = nodeA.services.signInitialTransaction(tx)
val signedByAB = nodeB.services.addSignature(signedByA) val signedByAB = nodeB.services.addSignature(signedByA)

View File

@ -23,6 +23,7 @@ import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
@ -131,9 +132,6 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
return ScheduledActivity(flowLogicRef, instant) return ScheduledActivity(flowLogicRef, instant)
} }
override val contract: Contract
get() = throw UnsupportedOperationException()
} }
class TestFlowLogic(val increment: Int = 1) : FlowLogic<Unit>() { class TestFlowLogic(val increment: Int = 1) : FlowLogic<Unit>() {
@ -282,7 +280,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.legalIdentity) val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.legalIdentity)
val builder = TransactionBuilder(null).apply { val builder = TransactionBuilder(null).apply {
addOutputState(state, DUMMY_NOTARY) addOutputState(state, DUMMY_PROGRAM_ID, DUMMY_NOTARY)
addCommand(Command(), freshKey) addCommand(Command(), freshKey)
} }
val usefulTX = services.signInitialTransaction(builder, freshKey) val usefulTX = services.signInitialTransaction(builder, freshKey)

View File

@ -21,6 +21,7 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.dummyCommand import net.corda.testing.dummyCommand
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
@ -46,8 +47,7 @@ class ScheduledFlowTests {
val source: Party, val source: Party,
val destination: Party, val destination: Party,
val processed: Boolean = false, val processed: Boolean = false,
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState {
override val contract: Contract = DummyContract()) : SchedulableState, LinearState {
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
if (!processed) { if (!processed) {
val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef)
@ -68,7 +68,7 @@ class ScheduledFlowTests {
val notary = serviceHub.networkMapCache.getAnyNotary() val notary = serviceHub.networkMapCache.getAnyNotary()
val builder = TransactionBuilder(notary) val builder = TransactionBuilder(notary)
.addOutputState(scheduledState) .addOutputState(scheduledState, DUMMY_PROGRAM_ID)
.addCommand(dummyCommand(serviceHub.legalIdentityKey)) .addCommand(dummyCommand(serviceHub.legalIdentityKey))
val tx = serviceHub.signInitialTransaction(builder) val tx = serviceHub.signInitialTransaction(builder)
subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity))) subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity)))
@ -90,7 +90,7 @@ class ScheduledFlowTests {
val newStateOutput = scheduledState.copy(processed = true) val newStateOutput = scheduledState.copy(processed = true)
val builder = TransactionBuilder(notary) val builder = TransactionBuilder(notary)
.addInputState(state) .addInputState(state)
.addOutputState(newStateOutput) .addOutputState(newStateOutput, DUMMY_PROGRAM_ID)
.addCommand(dummyCommand(serviceHub.legalIdentityKey)) .addCommand(dummyCommand(serviceHub.legalIdentityKey))
val tx = serviceHub.signInitialTransaction(builder) val tx = serviceHub.signInitialTransaction(builder)
subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination)))

View File

@ -12,6 +12,7 @@ import net.corda.node.services.api.SchemaService
import net.corda.node.utilities.DatabaseTransactionManager import net.corda.node.utilities.DatabaseTransactionManager
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
@ -72,9 +73,6 @@ class HibernateObserverTests {
throw UnsupportedOperationException() throw UnsupportedOperationException()
} }
override val contract: Contract
get() = throw UnsupportedOperationException()
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
} }
@ -101,7 +99,7 @@ class HibernateObserverTests {
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
val observer = HibernateObserver(rawUpdatesPublisher, database.hibernateConfig) val observer = HibernateObserver(rawUpdatesPublisher, database.hibernateConfig)
database.transaction { database.transaction {
rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), DUMMY_PROGRAM_ID, MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0)))))
val parentRowCountResult = DatabaseTransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery() val parentRowCountResult = DatabaseTransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery()
parentRowCountResult.next() parentRowCountResult.next()
val parentRows = parentRowCountResult.getInt(1) val parentRows = parentRowCountResult.getInt(1)

View File

@ -31,6 +31,7 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.persistence.checkpoints import net.corda.node.services.persistence.checkpoints
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
@ -599,7 +600,7 @@ class FlowFrameworkTests {
@Test @Test
fun `wait for transaction`() { fun `wait for transaction`() {
val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity)
.addOutputState(DummyState()) .addOutputState(DummyState(), DUMMY_PROGRAM_ID)
.addCommand(dummyCommand(node1.services.legalIdentityKey)) .addCommand(dummyCommand(node1.services.legalIdentityKey))
val stx = node1.services.signInitialTransaction(ptx) val stx = node1.services.signInitialTransaction(ptx)
@ -614,7 +615,7 @@ class FlowFrameworkTests {
@Test @Test
fun `committer throws exception before calling the finality flow`() { fun `committer throws exception before calling the finality flow`() {
val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity)
.addOutputState(DummyState()) .addOutputState(DummyState(), DUMMY_PROGRAM_ID)
.addCommand(dummyCommand()) .addCommand(dummyCommand())
val stx = node1.services.signInitialTransaction(ptx) val stx = node1.services.signInitialTransaction(ptx)
@ -631,7 +632,7 @@ class FlowFrameworkTests {
@Test @Test
fun `verify vault query service is tokenizable by force checkpointing within a flow`() { fun `verify vault query service is tokenizable by force checkpointing within a flow`() {
val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity)
.addOutputState(DummyState()) .addOutputState(DummyState(), DUMMY_PROGRAM_ID)
.addCommand(dummyCommand(node1.services.legalIdentityKey)) .addCommand(dummyCommand(node1.services.legalIdentityKey))
val stx = node1.services.signInitialTransaction(ptx) val stx = node1.services.signInitialTransaction(ptx)

View File

@ -231,8 +231,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
// Issue a linear state // Issue a linear state
val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand(notaryServices.legalIdentityKey)) addCommand(dummyCommand(notaryServices.legalIdentityKey))
} }
val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder) val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder)
@ -252,7 +252,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
val dummyIssue = val dummyIssue =
database.transaction { // Issue a linear state database.transaction { // Issue a linear state
val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY) val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY)
.addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))).addCommand(dummyCommand(notaryServices.legalIdentityKey)) .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
.addCommand(dummyCommand(notaryServices.legalIdentityKey))
val dummyIssuePtx = notaryServices.signInitialTransaction(dummyIssueBuilder) val dummyIssuePtx = notaryServices.signInitialTransaction(dummyIssueBuilder)
val dummyIssue = services.addSignature(dummyIssuePtx) val dummyIssue = services.addSignature(dummyIssuePtx)
@ -266,7 +267,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
// Move the same state // Move the same state
val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY) val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY)
.addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
.addInputState(dummyIssue.tx.outRef<LinearState>(0)) .addInputState(dummyIssue.tx.outRef<LinearState>(0))
.addCommand(dummyCommand(notaryServices.legalIdentityKey)) .addCommand(dummyCommand(notaryServices.legalIdentityKey))
@ -340,8 +341,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
// Create a txn consuming different contract types // Create a txn consuming different contract types
val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity))) addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)), DUMMY_DEAL_PROGRAM_ID)
addInputState(linearStates.first()) addInputState(linearStates.first())
addInputState(deals.first()) addInputState(deals.first())
addCommand(dummyCommand(notaryServices.legalIdentityKey)) addCommand(dummyCommand(notaryServices.legalIdentityKey))

View File

@ -116,7 +116,7 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
// Create a trivial transaction with an output that describes the attachment, and the attachment itself // Create a trivial transaction with an output that describes the attachment, and the attachment itself
val ptx = TransactionBuilder(notary) val ptx = TransactionBuilder(notary)
.addOutputState(AttachmentContract.State(hash)) .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID)
.addCommand(AttachmentContract.Command, serviceHub.legalIdentityKey) .addCommand(AttachmentContract.Command, serviceHub.legalIdentityKey)
.addAttachment(hash) .addAttachment(hash)
@ -178,6 +178,8 @@ private fun printHelp(parser: OptionParser) {
parser.printHelpOn(System.out) parser.printHelpOn(System.out)
} }
val ATTACHMENT_PROGRAM_ID = "net.corda.attachmentdemo.AttachmentContract"
class AttachmentContract : Contract { class AttachmentContract : Contract {
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
val state = tx.outputsOfType<AttachmentContract.State>().single() val state = tx.outputsOfType<AttachmentContract.State>().single()
@ -188,7 +190,6 @@ class AttachmentContract : Contract {
object Command : TypeOnlyCommandData() object Command : TypeOnlyCommandData()
data class State(val hash: SecureHash.SHA256) : ContractState { data class State(val hash: SecureHash.SHA256) : ContractState {
override val contract: Contract = AttachmentContract()
override val participants: List<AbstractParty> = emptyList() override val participants: List<AbstractParty> = emptyList()
} }
} }

View File

@ -20,7 +20,7 @@ import java.math.RoundingMode
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
val IRS_PROGRAM_ID = InterestRateSwap() val IRS_PROGRAM_ID = "net.corda.irs.contract.InterestRateSwap"
// This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion // This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion
@CordaSerializable @CordaSerializable
@ -605,9 +605,6 @@ class InterestRateSwap : Contract {
val common: Common, val common: Common,
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
) : FixableDealState, SchedulableState { ) : FixableDealState, SchedulableState {
override val contract = IRS_PROGRAM_ID
override val oracleType: ServiceType override val oracleType: ServiceType
get() = NodeInterestRates.Oracle.type get() = NodeInterestRates.Oracle.type
@ -627,7 +624,7 @@ class InterestRateSwap : Contract {
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary) override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary)
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix) InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, IRS_PROGRAM_ID, oldState.state.notary), oldState.ref), fix)
} }
override fun nextFixingOf(): FixOf? { override fun nextFixingOf(): FixOf? {
@ -721,7 +718,7 @@ class InterestRateSwap : Contract {
// Put all the above into a new State object. // Put all the above into a new State object.
val state = State(fixedLeg, floatingLeg, newCalculation, common) val state = State(fixedLeg, floatingLeg, newCalculation, common)
return TransactionBuilder(notary).withItems(state, Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey))) return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey)))
} }
private fun calcFixingDate(date: LocalDate, fixingPeriodOffset: Int, calendar: BusinessCalendar): LocalDate { private fun calcFixingDate(date: LocalDate, fixingPeriodOffset: Int, calendar: BusinessCalendar): LocalDate {
@ -736,6 +733,7 @@ class InterestRateSwap : Contract {
val fixedRate = FixedRate(RatioUnit(fixing.value)) val fixedRate = FixedRate(RatioUnit(fixing.value))
tx.addOutputState( tx.addOutputState(
irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.of.forDay, fixedRate)), irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.of.forDay, fixedRate)),
irs.state.contract,
irs.state.notary irs.state.notary
) )
tx.addCommand(Commands.Refix(fixing), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey)) tx.addCommand(Commands.Refix(fixing), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey))

View File

@ -3,7 +3,6 @@ package net.corda.irs.api
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.contracts.`with notary`
import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -15,10 +14,7 @@ import net.corda.core.utilities.getX500Name
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.contracts.Fix import net.corda.finance.contracts.Fix
import net.corda.finance.contracts.FixOf import net.corda.finance.contracts.FixOf
import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.*
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.`issued by`
import net.corda.finance.contracts.asset.`owned by`
import net.corda.irs.flows.RatesFixFlow import net.corda.irs.flows.RatesFixFlow
import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
@ -246,7 +242,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
} }
private fun makePartialTX() = TransactionBuilder(DUMMY_NOTARY).withItems( private fun makePartialTX() = TransactionBuilder(DUMMY_NOTARY).withItems(
1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE `with notary` DUMMY_NOTARY) TransactionState(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE, CASH_PROGRAM_ID, DUMMY_NOTARY))
private fun makeFullTx() = makePartialTX().withItems(dummyCommand()) private fun makeFullTx() = makePartialTX().withItems(dummyCommand())
} }

View File

@ -15,6 +15,7 @@ import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import net.corda.irs.contract.IRS_PROGRAM_ID
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
return when (irsSelect) { return when (irsSelect) {
@ -369,7 +370,7 @@ class IRSTests : TestDependencyInjectionBase() {
return ledger(initialiseSerialization = false) { return ledger(initialiseSerialization = false) {
transaction("Agreement") { transaction("Agreement") {
output("irs post agreement") { singleIRS() } output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -378,7 +379,7 @@ class IRSTests : TestDependencyInjectionBase() {
transaction("Fix") { transaction("Fix") {
input("irs post agreement") input("irs post agreement")
val postAgreement = "irs post agreement".output<InterestRateSwap.State>() val postAgreement = "irs post agreement".output<InterestRateSwap.State>()
output("irs post first fixing") { output(IRS_PROGRAM_ID, "irs post first fixing") {
postAgreement.copy( postAgreement.copy(
postAgreement.fixedLeg, postAgreement.fixedLeg,
postAgreement.floatingLeg, postAgreement.floatingLeg,
@ -399,8 +400,8 @@ class IRSTests : TestDependencyInjectionBase() {
fun `ensure failure occurs when there are inbound states for an agreement command`() { fun `ensure failure occurs when there are inbound states for an agreement command`() {
val irs = singleIRS() val irs = singleIRS()
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
input { irs } input(IRS_PROGRAM_ID, irs)
output("irs post agreement") { irs } output(IRS_PROGRAM_ID, "irs post agreement", irs)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are no in states for an agreement" this `fails with` "There are no in states for an agreement"
@ -412,9 +413,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val emptySchedule = mutableMapOf<LocalDate, FixedRatePaymentEvent>() val emptySchedule = mutableMapOf<LocalDate, FixedRatePaymentEvent>()
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule)))
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are events in the fix schedule" this `fails with` "There are events in the fix schedule"
@ -426,9 +425,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val emptySchedule = mutableMapOf<LocalDate, FloatingRatePaymentEvent>() val emptySchedule = mutableMapOf<LocalDate, FloatingRatePaymentEvent>()
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule)))
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are events in the float schedule" this `fails with` "There are events in the float schedule"
@ -439,18 +436,14 @@ class IRSTests : TestDependencyInjectionBase() {
fun `ensure notionals are non zero`() { fun `ensure notionals are non zero`() {
val irs = singleIRS() val irs = singleIRS()
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0))))
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All notionals must be non zero" this `fails with` "All notionals must be non zero"
} }
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0))))
irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All notionals must be non zero" this `fails with` "All notionals must be non zero"
@ -462,9 +455,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1")))) val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1"))))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS)
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The fixed leg rate must be positive" this `fails with` "The fixed leg rate must be positive"
@ -479,9 +470,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY")))) val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY"))))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS)
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The currency of the notionals must be the same" this `fails with` "The currency of the notionals must be the same"
@ -493,9 +482,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token))) val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token)))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS)
modifiedIRS
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All leg notionals must be the same" this `fails with` "All leg notionals must be the same"
@ -507,9 +494,7 @@ class IRSTests : TestDependencyInjectionBase() {
val irs = singleIRS() val irs = singleIRS()
val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1))) val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1)))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS1)
modifiedIRS1
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the fixed leg" this `fails with` "The effective date is before the termination date for the fixed leg"
@ -517,9 +502,7 @@ class IRSTests : TestDependencyInjectionBase() {
val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1))) val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1)))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS2)
modifiedIRS2
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the floating leg" this `fails with` "The effective date is before the termination date for the floating leg"
@ -532,9 +515,7 @@ class IRSTests : TestDependencyInjectionBase() {
val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1))) val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1)))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS3)
modifiedIRS3
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The termination dates are aligned" this `fails with` "The termination dates are aligned"
@ -543,9 +524,7 @@ class IRSTests : TestDependencyInjectionBase() {
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1))) val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output { output(IRS_PROGRAM_ID, modifiedIRS4)
modifiedIRS4
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective dates are aligned" this `fails with` "The effective dates are aligned"
@ -559,7 +538,7 @@ class IRSTests : TestDependencyInjectionBase() {
val bd = BigDecimal("0.0063518") val bd = BigDecimal("0.0063518")
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
output("irs post agreement") { singleIRS() } output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() }
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
@ -572,10 +551,7 @@ class IRSTests : TestDependencyInjectionBase() {
oldIRS.common) oldIRS.common)
transaction(initialiseSerialization = false) { transaction(initialiseSerialization = false) {
input { input(IRS_PROGRAM_ID, oldIRS)
oldIRS
}
// Templated tweak for reference. A corrent fixing applied should be ok // Templated tweak for reference. A corrent fixing applied should be ok
tweak { tweak {
@ -583,7 +559,7 @@ class IRSTests : TestDependencyInjectionBase() {
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))
} }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
output { newIRS } output(IRS_PROGRAM_ID, newIRS)
this.verifies() this.verifies()
} }
@ -591,7 +567,7 @@ class IRSTests : TestDependencyInjectionBase() {
tweak { tweak {
command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
output { oldIRS } output(IRS_PROGRAM_ID, oldIRS)
this `fails with` "There is at least one difference in the IRS floating leg payment schedules" this `fails with` "There is at least one difference in the IRS floating leg payment schedules"
} }
@ -604,7 +580,7 @@ class IRSTests : TestDependencyInjectionBase() {
val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey]
val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY")))
output { output(IRS_PROGRAM_ID) {
newIRS.copy( newIRS.copy(
newIRS.fixedLeg, newIRS.fixedLeg,
newIRS.floatingLeg, newIRS.floatingLeg,
@ -624,7 +600,7 @@ class IRSTests : TestDependencyInjectionBase() {
val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key }
val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY")))
output { output(IRS_PROGRAM_ID) {
newIRS.copy( newIRS.copy(
newIRS.fixedLeg, newIRS.fixedLeg,
newIRS.floatingLeg, newIRS.floatingLeg,
@ -653,7 +629,7 @@ class IRSTests : TestDependencyInjectionBase() {
return ledger(initialiseSerialization = false) { return ledger(initialiseSerialization = false) {
transaction("Agreement") { transaction("Agreement") {
output("irs post agreement1") { output(IRS_PROGRAM_ID, "irs post agreement1") {
irs.copy( irs.copy(
irs.fixedLeg, irs.fixedLeg,
irs.floatingLeg, irs.floatingLeg,
@ -667,7 +643,7 @@ class IRSTests : TestDependencyInjectionBase() {
} }
transaction("Agreement") { transaction("Agreement") {
output("irs post agreement2") { output(IRS_PROGRAM_ID, "irs post agreement2") {
irs.copy( irs.copy(
linearId = UniqueIdentifier("t2"), linearId = UniqueIdentifier("t2"),
fixedLeg = irs.fixedLeg, fixedLeg = irs.fixedLeg,
@ -685,7 +661,7 @@ class IRSTests : TestDependencyInjectionBase() {
input("irs post agreement1") input("irs post agreement1")
input("irs post agreement2") input("irs post agreement2")
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>() val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>()
output("irs post first fixing1") { output(IRS_PROGRAM_ID, "irs post first fixing1") {
postAgreement1.copy( postAgreement1.copy(
postAgreement1.fixedLeg, postAgreement1.fixedLeg,
postAgreement1.floatingLeg, postAgreement1.floatingLeg,
@ -694,7 +670,7 @@ class IRSTests : TestDependencyInjectionBase() {
) )
} }
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>() val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>()
output("irs post first fixing2") { output(IRS_PROGRAM_ID, "irs post first fixing2") {
postAgreement2.copy( postAgreement2.copy(
postAgreement2.fixedLeg, postAgreement2.fixedLeg,
postAgreement2.floatingLeg, postAgreement2.floatingLeg,

View File

@ -14,22 +14,21 @@ import net.corda.core.transactions.TransactionBuilder
@StartableByRPC @StartableByRPC
class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic<SignedTransaction>() { class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic<SignedTransaction>() {
val DO_NOTHING_PROGRAM_ID = "net.corda.notarydemo.flows.DummyIssueAndMove.DoNothingContract"
object DoNothingContract : Contract { object DoNothingContract : Contract {
override fun verify(tx: LedgerTransaction) {} override fun verify(tx: LedgerTransaction) {}
} }
data class DummyCommand(val dummy: Int = 0): CommandData data class DummyCommand(val dummy: Int = 0): CommandData
data class State(override val participants: List<AbstractParty>, private val discriminator: Int) : ContractState { data class State(override val participants: List<AbstractParty>, private val discriminator: Int) : ContractState
override val contract = DoNothingContract
}
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
// Self issue an asset // Self issue an asset
val state = State(listOf(serviceHub.myInfo.legalIdentity), discriminator) val state = State(listOf(serviceHub.myInfo.legalIdentity), discriminator)
val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply {
addOutputState(state) addOutputState(state, DO_NOTHING_PROGRAM_ID)
addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey))
}) })
serviceHub.recordTransactions(issueTx) serviceHub.recordTransactions(issueTx)
@ -37,7 +36,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode:
// We don't check signatures because we know that the notary's signature is missing // We don't check signatures because we know that the notary's signature is missing
return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply {
addInputState(issueTx.tx.outRef<ContractState>(0)) addInputState(issueTx.tx.outRef<ContractState>(0))
addOutputState(state.copy(participants = listOf(counterpartyNode))) addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID)
addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey))
}) })
} }

View File

@ -1,6 +1,7 @@
package net.corda.vega.contracts package net.corda.vega.contracts
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
@ -9,6 +10,8 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.finance.contracts.DealState import net.corda.finance.contracts.DealState
import java.security.PublicKey import java.security.PublicKey
val IRS_PROGRAM_ID = "net.corda.vega.contracts.OGTrade"
/** /**
* Represents an OpenGamma IRS between two parties. Does not implement any fixing functionality. * Represents an OpenGamma IRS between two parties. Does not implement any fixing functionality.
* *
@ -17,13 +20,12 @@ import java.security.PublicKey
data class IRSState(val swap: SwapData, data class IRSState(val swap: SwapData,
val buyer: AbstractParty, val buyer: AbstractParty,
val seller: AbstractParty, val seller: AbstractParty,
override val contract: OGTrade,
override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState { override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState {
val ref: String get() = linearId.externalId!! // Same as the constructor for UniqueIdentified val ref: String get() = linearId.externalId!! // Same as the constructor for UniqueIdentified
override val participants: List<AbstractParty> get() = listOf(buyer, seller) override val participants: List<AbstractParty> get() = listOf(buyer, seller)
override fun generateAgreement(notary: Party): TransactionBuilder { override fun generateAgreement(notary: Party): TransactionBuilder {
val state = IRSState(swap, buyer, seller, OGTrade()) val state = IRSState(swap, buyer, seller)
return TransactionBuilder(notary).withItems(state, Command(OGTrade.Commands.Agree(), participants.map { it.owningKey })) return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(OGTrade.Commands.Agree(), participants.map { it.owningKey }))
} }
} }

View File

@ -14,12 +14,13 @@ import java.time.LocalDate
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
val PORTFOLIO_SWAP_PROGRAM_ID = "net.corda.vega.contracts.PortfolioSwap"
/** /**
* Represents an aggregate set of trades agreed between two parties and a possible valuation of that portfolio at a * Represents an aggregate set of trades agreed between two parties and a possible valuation of that portfolio at a
* given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio. * given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio.
*/ */
data class PortfolioState(val portfolio: List<StateRef>, data class PortfolioState(val portfolio: List<StateRef>,
override val contract: PortfolioSwap,
private val _parties: Pair<AbstractParty, AbstractParty>, private val _parties: Pair<AbstractParty, AbstractParty>,
val valuationDate: LocalDate, val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null, val valuation: PortfolioValuation? = null,
@ -38,7 +39,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
} }
override fun generateAgreement(notary: Party): TransactionBuilder { override fun generateAgreement(notary: Party): TransactionBuilder {
return TransactionBuilder(notary).withItems(copy(), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey })) return TransactionBuilder(notary).withItems(StateAndContract(copy(), PORTFOLIO_SWAP_PROGRAM_ID), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey }))
} }
override fun generateRevision(notary: Party, oldState: StateAndRef<*>, updatedValue: Update): TransactionBuilder { override fun generateRevision(notary: Party, oldState: StateAndRef<*>, updatedValue: Update): TransactionBuilder {
@ -48,7 +49,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
val tx = TransactionBuilder(notary) val tx = TransactionBuilder(notary)
tx.addInputState(oldState) tx.addInputState(oldState)
tx.addOutputState(copy(portfolio = portfolio, valuation = valuation)) tx.addOutputState(copy(portfolio = portfolio, valuation = valuation), PORTFOLIO_SWAP_PROGRAM_ID)
tx.addCommand(PortfolioSwap.Commands.Update(), participants.map { it.owningKey }) tx.addCommand(PortfolioSwap.Commands.Update(), participants.map { it.owningKey })
return tx return tx
} }

View File

@ -32,7 +32,7 @@ object IRSTradeFlow {
} else { } else {
Pair(otherParty, myIdentity) Pair(otherParty, myIdentity)
} }
val offer = IRSState(swap, buyer, seller, OGTrade()) val offer = IRSState(swap, buyer, seller)
logger.info("Handshake finished, sending IRS trade offer message") logger.info("Handshake finished, sending IRS trade offer message")
val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it } val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it }

View File

@ -84,7 +84,7 @@ object SimmFlow {
private fun agreePortfolio(portfolio: Portfolio) { private fun agreePortfolio(portfolio: Portfolio) {
logger.info("Agreeing portfolio") logger.info("Agreeing portfolio")
val parties = Pair(myIdentity, otherParty) val parties = Pair(myIdentity, otherParty)
val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate) val portfolioState = PortfolioState(portfolio.refs, parties, valuationDate)
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
logger.info("Awaiting two party deal acceptor") logger.info("Awaiting two party deal acceptor")

View File

@ -6,6 +6,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
@ -35,7 +36,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() {
val notaryServices = MockServices(DUMMY_NOTARY_KEY) val notaryServices = MockServices(DUMMY_NOTARY_KEY)
val originBuilder = TransactionBuilder(DUMMY_NOTARY) val originBuilder = TransactionBuilder(DUMMY_NOTARY)
.addOutputState(DummyState(random31BitValue())) .addOutputState(DummyState(random31BitValue()), DUMMY_PROGRAM_ID)
.addCommand(command, MEGA_CORP_PUBKEY) .addCommand(command, MEGA_CORP_PUBKEY)
val originPtx = megaCorpServices.signInitialTransaction(originBuilder) val originPtx = megaCorpServices.signInitialTransaction(originBuilder)

View File

@ -9,6 +9,7 @@ import net.corda.core.node.ServiceHub
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.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import java.io.InputStream import java.io.InputStream
import java.security.KeyPair import java.security.KeyPair
@ -119,8 +120,8 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder.addInputState(StateAndRef(state, stateRef)) transactionBuilder.addInputState(StateAndRef(state, stateRef))
} }
override fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) { override fun _output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) {
transactionBuilder.addOutputState(contractState, notary, encumbrance) transactionBuilder.addOutputState(contractState, contractClassName, notary, encumbrance)
if (label != null) { if (label != null) {
if (label in labelToIndexMap) { if (label in labelToIndexMap) {
throw DuplicateOutputLabel(label) throw DuplicateOutputLabel(label)
@ -276,7 +277,7 @@ data class TestLedgerDSLInterpreter private constructor(
private fun fillTransaction(transactionBuilder: TransactionBuilder) { private fun fillTransaction(transactionBuilder: TransactionBuilder) {
if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand()) if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand())
if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) { if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) {
transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE)) transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE), DUMMY_PROGRAM_ID)
} }
} }

View File

@ -34,8 +34,9 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
* @param notary The associated notary. * @param notary The associated notary.
* @param encumbrance The position of the encumbrance state. * @param encumbrance The position of the encumbrance state.
* @param contractState The state itself. * @param contractState The state itself.
* @params contractClassName The class name of the contract that verifies this state.
*/ */
fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) fun _output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, contractState: ContractState)
/** /**
* Adds an [Attachment] reference to the transaction. * Adds an [Attachment] reference to the transaction.
@ -75,30 +76,30 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
* input to the current transaction. * input to the current transaction.
* @param state The state to be added. * @param state The state to be added.
*/ */
fun input(state: ContractState) { fun input(contractClassName: ContractClassName, state: ContractState) {
val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) { val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) {
output { state } output(contractClassName) { state }
} }
input(transaction.outRef<ContractState>(0).ref) input(transaction.outRef<ContractState>(0).ref)
} }
fun input(stateClosure: () -> ContractState) = input(stateClosure()) fun input(contractClassName: ContractClassName, stateClosure: () -> ContractState) = input(contractClassName, stateClosure())
/** /**
* @see TransactionDSLInterpreter._output * @see TransactionDSLInterpreter._output
*/ */
@JvmOverloads @JvmOverloads
fun output(label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) = fun output(contractClassName: ContractClassName, label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) =
_output(label, notary, encumbrance, contractStateClosure()) _output(contractClassName, label, notary, encumbrance, contractStateClosure())
/** /**
* @see TransactionDSLInterpreter._output * @see TransactionDSLInterpreter._output
*/ */
fun output(label: String, contractState: ContractState) = fun output(contractClassName: ContractClassName, label: String, contractState: ContractState) =
_output(label, DUMMY_NOTARY, null, contractState) _output(contractClassName, label, DUMMY_NOTARY, null, contractState)
fun output(contractState: ContractState) = fun output(contractClassName: ContractClassName, contractState: ContractState) =
_output(null, DUMMY_NOTARY, null, contractState) _output(contractClassName,null, DUMMY_NOTARY, null, contractState)
/** /**
* @see TransactionDSLInterpreter._command * @see TransactionDSLInterpreter._command

View File

@ -8,7 +8,7 @@ import net.corda.core.transactions.TransactionBuilder
// The dummy contract doesn't do anything useful. It exists for testing purposes, but has to be serializable // The dummy contract doesn't do anything useful. It exists for testing purposes, but has to be serializable
val DUMMY_PROGRAM_ID = DummyContract() val DUMMY_PROGRAM_ID = "net.corda.testing.contracts.DummyContract"
data class DummyContract(val blank: Any? = null) : Contract { data class DummyContract(val blank: Any? = null) : Contract {
interface State : ContractState { interface State : ContractState {
@ -16,7 +16,6 @@ data class DummyContract(val blank: Any? = null) : Contract {
} }
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: AbstractParty) : OwnableState, State { data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: AbstractParty) : OwnableState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
@ -30,7 +29,6 @@ data class DummyContract(val blank: Any? = null) : Contract {
*/ */
data class MultiOwnerState(override val magicNumber: Int = 0, data class MultiOwnerState(override val magicNumber: Int = 0,
val owners: List<AbstractParty>) : ContractState, State { val owners: List<AbstractParty>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<AbstractParty> get() = owners override val participants: List<AbstractParty> get() = owners
} }
@ -49,10 +47,10 @@ data class DummyContract(val blank: Any? = null) : Contract {
val owners = listOf(owner) + otherOwners val owners = listOf(owner) + otherOwners
return if (owners.size == 1) { return if (owners.size == 1) {
val state = SingleOwnerState(magicNumber, owners.first().party) val state = SingleOwnerState(magicNumber, owners.first().party)
TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey)) TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), Command(Commands.Create(), owners.first().party.owningKey))
} else { } else {
val state = MultiOwnerState(magicNumber, owners.map { it.party }) val state = MultiOwnerState(magicNumber, owners.map { it.party })
TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey })) TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), Command(Commands.Create(), owners.map { it.party.owningKey }))
} }
} }
@ -64,7 +62,7 @@ data class DummyContract(val blank: Any? = null) : Contract {
return TransactionBuilder(notary = priors[0].state.notary).withItems( return TransactionBuilder(notary = priors[0].state.notary).withItems(
/* INPUTS */ *priors.toTypedArray(), /* INPUTS */ *priors.toTypedArray(),
/* COMMAND */ Command(cmd, priorState.owner.owningKey), /* COMMAND */ Command(cmd, priorState.owner.owningKey),
/* OUTPUT */ state /* OUTPUT */ StateAndContract(state, DUMMY_PROGRAM_ID)
) )
} }
} }

View File

@ -8,17 +8,16 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2() val DUMMY_V2_PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2"
/** /**
* Dummy contract state for testing of the upgrade process. * Dummy contract state for testing of the upgrade process.
*/ */
// DOCSTART 1 // DOCSTART 1
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> { class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
override val legacyContract = DummyContract::class.java override val legacyContract = DummyContract::class.java.name
data class State(val magicNumber: Int = 0, val owners: List<AbstractParty>) : ContractState { data class State(val magicNumber: Int = 0, val owners: List<AbstractParty>) : ContractState {
override val contract = DUMMY_V2_PROGRAM_ID
override val participants: List<AbstractParty> = owners override val participants: List<AbstractParty> = owners
} }
@ -51,8 +50,8 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
return Pair(TransactionBuilder(notary).apply { return Pair(TransactionBuilder(notary).apply {
states.forEach { states.forEach {
addInputState(it) addInputState(it)
addOutputState(upgrade(it.state.data)) addOutputState(upgrade(it.state.data), DUMMY_V2_PROGRAM_ID)
addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.map { it.owningKey }.toList()) addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID), signees.map { it.owningKey }.toList())
} }
}.toWireTransaction(), signees) }.toWireTransaction(), signees)
} }

View File

@ -14,17 +14,17 @@ import net.corda.finance.contracts.DealState
import net.corda.testing.schemas.DummyDealStateSchemaV1 import net.corda.testing.schemas.DummyDealStateSchemaV1
import java.security.PublicKey import java.security.PublicKey
val DUMMY_DEAL_PROGRAM_ID = "net.corda.testing.contracts.DummyDealContract"
class DummyDealContract : Contract { class DummyDealContract : Contract {
override fun verify(tx: LedgerTransaction) {} override fun verify(tx: LedgerTransaction) {}
data class State( data class State(
override val contract: Contract,
override val participants: List<AbstractParty>, override val participants: List<AbstractParty>,
override val linearId: UniqueIdentifier) : DealState, QueryableState override val linearId: UniqueIdentifier) : DealState, QueryableState
{ {
constructor(contract: Contract = DummyDealContract(), constructor(participants: List<AbstractParty> = listOf(),
participants: List<AbstractParty> = listOf(), ref: String) : this(participants, UniqueIdentifier(ref))
ref: String) : this(contract, participants, UniqueIdentifier(ref))
override fun generateAgreement(notary: Party): TransactionBuilder { override fun generateAgreement(notary: Party): TransactionBuilder {
throw UnsupportedOperationException("not implemented") throw UnsupportedOperationException("not implemented")

View File

@ -16,6 +16,8 @@ import net.corda.testing.schemas.DummyLinearStateSchemaV2
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneOffset.UTC import java.time.ZoneOffset.UTC
val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract"
class DummyLinearContract : Contract { class DummyLinearContract : Contract {
override fun verify(tx: LedgerTransaction) { override fun verify(tx: LedgerTransaction) {
val inputs = tx.inputs.map { it.state.data }.filterIsInstance<State>() val inputs = tx.inputs.map { it.state.data }.filterIsInstance<State>()
@ -31,16 +33,13 @@ class DummyLinearContract : Contract {
data class State( data class State(
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val contract: Contract = DummyLinearContract(),
override val participants: List<AbstractParty> = listOf(), override val participants: List<AbstractParty> = listOf(),
val linearString: String = "ABC", val linearString: String = "ABC",
val linearNumber: Long = 123L, val linearNumber: Long = 123L,
val linearTimestamp: java.time.Instant = LocalDateTime.now().toInstant(UTC), val linearTimestamp: java.time.Instant = LocalDateTime.now().toInstant(UTC),
val linearBoolean: Boolean = true, val linearBoolean: Boolean = true,
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState, QueryableState { val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState, QueryableState {
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(DummyLinearStateSchemaV1, DummyLinearStateSchemaV2) override fun supportedSchemas(): Iterable<MappedSchema> = listOf(DummyLinearStateSchemaV1, DummyLinearStateSchemaV2)
override fun generateMappedObject(schema: MappedSchema): PersistentState { override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) { return when (schema) {
is DummyLinearStateSchemaV1 -> DummyLinearStateSchemaV1.PersistentDummyLinearState( is DummyLinearStateSchemaV1 -> DummyLinearStateSchemaV1.PersistentDummyLinearState(

View File

@ -7,6 +7,5 @@ import net.corda.core.identity.AbstractParty
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract]. * Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
*/ */
data class DummyState(val magicNumber: Int = 0) : ContractState { data class DummyState(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<AbstractParty> get() = emptyList() override val participants: List<AbstractParty> get() = emptyList()
} }

View File

@ -41,7 +41,7 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
val transactions: List<SignedTransaction> = dealIds.map { val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state // Issue a deal state
val dummyIssue = TransactionBuilder(notary = notary).apply { val dummyIssue = TransactionBuilder(notary = notary).apply {
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me))) addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID)
addCommand(dummyCommand()) addCommand(dummyCommand())
} }
val stx = signInitialTransaction(dummyIssue) val stx = signInitialTransaction(dummyIssue)
@ -80,7 +80,7 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
linearString = linearString, linearString = linearString,
linearNumber = linearNumber, linearNumber = linearNumber,
linearBoolean = linearBoolean, linearBoolean = linearBoolean,
linearTimestamp = linearTimestamp)) linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand()) addCommand(dummyCommand())
} }
@ -221,7 +221,7 @@ fun <T : LinearState> ServiceHub.consumeAndProduce(stateAndRef: StateAndRef<T>,
// Create a txn consuming different contract types // Create a txn consuming different contract types
builder = TransactionBuilder(notary = notary).apply { builder = TransactionBuilder(notary = notary).apply {
addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId, addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId,
participants = stateAndRef.state.data.participants)) participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand(notary.owningKey)) addCommand(dummyCommand(notary.owningKey))
} }
val producedTx = signInitialTransaction(builder, notary.owningKey) val producedTx = signInitialTransaction(builder, notary.owningKey)

View File

@ -134,8 +134,8 @@ class TransactionViewer : CordaView("Transactions") {
val searchField = SearchField(transactions, val searchField = SearchField(transactions,
"Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) }, "Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) },
"Input" to { tx, s -> tx.inputs.resolved.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } }, "Input" to { tx, s -> tx.inputs.resolved.any { it.state.contract.contains(s, true) } },
"Output" to { tx, s -> tx.outputs.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } }, "Output" to { tx, s -> tx.outputs.any { it.state.contract.contains(s, true) } },
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } }, "Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } },
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } }, "Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } },
"Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } } "Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } }
@ -207,7 +207,7 @@ class TransactionViewer : CordaView("Transactions") {
} }
private fun ObservableList<StateAndRef<ContractState>>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } } private fun ObservableList<StateAndRef<ContractState>>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } }
private fun ObservableList<StateAndRef<ContractState>>.toText() = map { it.contract().javaClass.simpleName }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString() private fun ObservableList<StateAndRef<ContractState>>.toText() = map { it.contract() }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString()
private class TransactionWidget : BorderPane() { private class TransactionWidget : BorderPane() {
private val partiallyResolvedTransactions by observableListReadOnly(TransactionDataModel::partiallyResolvedTransactions) private val partiallyResolvedTransactions by observableListReadOnly(TransactionDataModel::partiallyResolvedTransactions)
@ -299,7 +299,7 @@ class TransactionViewer : CordaView("Transactions") {
} }
} }
private fun StateAndRef<ContractState>.contract() = this.state.data.contract private fun StateAndRef<ContractState>.contract() = this.state.contract
} }

View File

@ -11,6 +11,7 @@ import net.corda.core.identity.Party
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
import net.corda.core.utilities.getX500Name import net.corda.core.utilities.getX500Name
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import java.math.BigInteger import java.math.BigInteger
@ -62,7 +63,7 @@ data class GeneratedLedger(
Generator.sequence( Generator.sequence(
outputs.map { output -> outputs.map { output ->
pickOneOrMaybeNew(identities, partyGenerator).map { notary -> pickOneOrMaybeNew(identities, partyGenerator).map { notary ->
TransactionState(output, notary, null) TransactionState(output, DUMMY_PROGRAM_ID, notary, null)
} }
} }
) )
@ -127,7 +128,7 @@ data class GeneratedLedger(
fun regularTransactionGenerator(inputNotary: Party, inputsToChooseFrom: List<StateAndRef<ContractState>>): Generator<Pair<WireTransaction, GeneratedLedger>> { fun regularTransactionGenerator(inputNotary: Party, inputsToChooseFrom: List<StateAndRef<ContractState>>): Generator<Pair<WireTransaction, GeneratedLedger>> {
val outputsGen = outputsGenerator.map { outputs -> val outputsGen = outputsGenerator.map { outputs ->
outputs.map { output -> outputs.map { output ->
TransactionState(output, inputNotary, null) TransactionState(output, DUMMY_PROGRAM_ID, inputNotary, null)
} }
} }
val inputsGen = Generator.sampleBernoulli(inputsToChooseFrom) val inputsGen = Generator.sampleBernoulli(inputsToChooseFrom)
@ -180,9 +181,7 @@ data class GeneratedLedger(
data class GeneratedState( data class GeneratedState(
val nonce: Long, val nonce: Long,
override val participants: List<AbstractParty> override val participants: List<AbstractParty>
) : ContractState { ) : ContractState
override val contract = DummyContract()
}
class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) { class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) {
override val id = bytes.sha256() override val id = bytes.sha256()