From bc6628a072746dad823a605d14ae3888bae16fab Mon Sep 17 00:00:00 2001 From: Clinton Date: Mon, 11 Sep 2017 16:44:18 +0100 Subject: [PATCH] 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. --- .idea/compiler.xml | 2 +- .../net/corda/core/contracts/ContractState.kt | 27 +++ .../net/corda/core/contracts/Structures.kt | 114 +---------- .../corda/core/contracts/TransactionState.kt | 74 +++++++ .../TransactionVerificationException.kt | 3 + .../corda/core/flows/ContractUpgradeFlow.kt | 26 +-- .../core/transactions/LedgerTransaction.kt | 10 +- .../core/transactions/TransactionBuilder.kt | 12 +- .../core/contracts/DummyContractV2Tests.kt | 8 +- .../contracts/LedgerTransactionQueryTests.kt | 11 +- .../contracts/TransactionEncumbranceTests.kt | 46 ++--- .../corda/core/contracts/TransactionTests.kt | 7 +- .../core/crypto/PartialMerkleTreeTest.kt | 7 +- .../core/flows/CollectSignaturesFlowTests.kt | 6 +- .../core/flows/ContractUpgradeFlowTest.kt | 6 +- .../net/corda/core/node/VaultUpdateTests.kt | 15 +- .../TransactionSerializationTests.kt | 11 +- docs/source/changelog.rst | 3 + .../java/net/corda/docs/FlowCookbookJava.java | 3 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 3 +- .../corda/docs/FxTransactionBuildTutorial.kt | 10 +- .../docs/WorkflowTransactionBuildTutorial.kt | 10 +- docs/source/hello-world-state.rst | 2 +- docs/source/tutorial-contract.rst | 2 +- .../contracts/universal/UniversalContract.kt | 8 +- .../corda/finance/contracts/universal/Cap.kt | 20 +- .../finance/contracts/universal/Caplet.kt | 10 +- .../contracts/universal/FXFwdTimeOption.kt | 14 +- .../finance/contracts/universal/FXSwap.kt | 48 ++--- .../corda/finance/contracts/universal/IRS.kt | 12 +- .../contracts/universal/RollOutTests.kt | 8 +- .../finance/contracts/universal/Swaption.kt | 2 +- .../contracts/universal/ZeroCouponBond.kt | 22 +- .../isolated/AnotherDummyContract.kt | 7 +- .../contracts/JavaCommercialPaper.java | 12 +- .../finance/contracts/CommercialPaper.kt | 8 +- .../net/corda/finance/contracts/asset/Cash.kt | 5 +- .../contracts/asset/CommodityContract.kt | 6 +- .../finance/contracts/asset/Obligation.kt | 21 +- .../corda/finance/flows/TwoPartyTradeFlow.kt | 2 +- .../contracts/asset/CashTestsJava.java | 15 +- .../finance/contracts/CommercialPaperTests.kt | 30 +-- .../finance/contracts/asset/CashTests.kt | 138 ++++++------- .../contracts/asset/DummyFungibleContract.kt | 1 - .../contracts/asset/ObligationTests.kt | 189 +++++++++--------- .../nodeapi/AttachmentClassLoaderTests.kt | 42 ++-- .../serialization/CordaClassResolverTests.kt | 2 +- .../amqp/SerializationOutputTests.kt | 8 +- .../resources/net/corda/nodeapi/isolated.jar | Bin 7907 -> 7977 bytes .../node/services/BFTNotaryServiceTests.kt | 5 +- .../node/services/RaftNotaryServiceTests.kt | 3 +- .../statemachine/LargeTransactionsTest.kt | 3 +- .../test/node/NodeStatePersistenceTests.kt | 13 +- .../upgrade/ContractUpgradeServiceImpl.kt | 2 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 17 +- .../corda/node/services/NotaryChangeTests.kt | 9 +- .../events/NodeSchedulerServiceTest.kt | 6 +- .../services/events/ScheduledFlowTests.kt | 8 +- .../services/schema/HibernateObserverTests.kt | 6 +- .../statemachine/FlowFrameworkTests.kt | 7 +- .../node/services/vault/VaultWithCashTest.kt | 13 +- .../corda/attachmentdemo/AttachmentDemo.kt | 5 +- .../main/kotlin/net/corda/irs/contract/IRS.kt | 10 +- .../corda/irs/api/NodeInterestRatesTest.kt | 8 +- .../kotlin/net/corda/irs/contract/IRSTests.kt | 76 +++---- .../notarydemo/flows/DummyIssueAndMove.kt | 9 +- .../net/corda/vega/contracts/IRSState.kt | 8 +- .../corda/vega/contracts/PortfolioState.kt | 7 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 2 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 2 +- .../traderdemo/TransactionGraphSearchTests.kt | 3 +- .../main/kotlin/net/corda/testing/TestDSL.kt | 7 +- .../testing/TransactionDSLInterpreter.kt | 21 +- .../corda/testing/contracts/DummyContract.kt | 10 +- .../testing/contracts/DummyContractV2.kt | 9 +- .../testing/contracts/DummyDealContract.kt | 8 +- .../testing/contracts/DummyLinearContract.kt | 5 +- .../net/corda/testing/contracts/DummyState.kt | 1 - .../corda/testing/contracts/VaultFiller.kt | 6 +- .../corda/explorer/views/TransactionViewer.kt | 8 +- .../net/corda/verifier/GeneratedLedger.kt | 9 +- 81 files changed, 669 insertions(+), 685 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/contracts/ContractState.kt create mode 100644 core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 652807fef9..9a2d07a3e4 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -109,4 +109,4 @@ - \ No newline at end of file + diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt new file mode 100644 index 0000000000..ab4bb2acb4 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt @@ -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 +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 820d87fe6a..9c93fa26d3 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -20,106 +20,6 @@ interface NamedByHash { 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 -} -// 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 @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.`with notary`(newNotary: Party) = withNotary(newNotary) - -infix fun T.withNotary(newNotary: Party) = TransactionState(this, newNotary) - /** * 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 @@ -250,7 +150,7 @@ data class StateAndRef(val state: TransactionState, va /** Filters a list of [StateAndRef] objects according to the type of the states */ inline fun Iterable>.filterStatesOfType(): List> { - 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 */ -data class UpgradeCommand(val upgradedContractClass: Class>) : CommandData +data class UpgradeCommand(val upgradedContractClass: ContractClassName) : CommandData // DOCSTART 6 /** 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. */ interface UpgradedContract : Contract { - val legacyContract: Class + val legacyContract: ContractClassName /** * 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." } } } + +/** + * 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) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt new file mode 100644 index 0000000000..05c241b2d3 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt @@ -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 @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) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index 6f42461e5c..5e5980d7a0 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -16,6 +16,9 @@ sealed class TransactionVerificationException(val txId: SecureHash, message: Str class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) : 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) : TransactionVerificationException(txId, "More than one notary", null) diff --git a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt index c9a1cbee02..b1e32b4be4 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt @@ -32,7 +32,7 @@ object ContractUpgradeFlow { @Suspendable override fun call(): Void? { 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.") } serviceHub.contractUpgradeService.storeAuthorisedContractUpgrade(stateAndRef.ref, upgradedContractClass) @@ -73,8 +73,8 @@ object ContractUpgradeFlow { return TransactionBuilder(stateRef.state.notary) .withItems( stateRef, - contractUpgrade.upgrade(stateRef.state.data), - Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants.map { it.owningKey }), + StateAndContract(contractUpgrade.upgrade(stateRef.state.data), upgradedContractClass.name), + Command(UpgradeCommand(upgradedContractClass.name), stateRef.state.data.participants.map { it.owningKey }), privacySalt ) } @@ -99,23 +99,23 @@ object ContractUpgradeFlow { @JvmStatic fun verify(tx: LedgerTransaction) { // Contract Upgrade transaction should have 1 input, 1 output and 1 command. - verify(tx.inputStates.single(), - tx.outputStates.single(), + verify(tx.inputs.single().state, + tx.outputs.single(), tx.commandsOfType().single()) } @JvmStatic - fun verify(input: ContractState, output: ContractState, commandData: Command) { + fun verify(input: TransactionState, output: TransactionState, commandData: Command) { val command = commandData.value - val participantKeys: Set = input.participants.map { it.owningKey }.toSet() + val participantKeys: Set = input.data.participants.map { it.owningKey }.toSet() val keysThatSigned: Set = commandData.signers.toSet() @Suppress("UNCHECKED_CAST") - val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract + val upgradedContract = javaClass.classLoader.loadClass(command.upgradedContractClass).newInstance() as UpgradedContract requireThat { "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys) - "Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract) - "Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass) - "Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input)) + "Inputs state reference the legacy contract" using (input.contract == upgradedContract.legacyContract) + "Outputs state reference the upgraded contract" using (output.contract == command.upgradedContractClass) + "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) } ContractUpgradeFlow.Acceptor.verify( - oldStateAndRef.state.data, - expectedTx.outRef(0).state.data, + oldStateAndRef.state, + expectedTx.outRef(0).state, expectedTx.toLedgerTransaction(serviceHub).commandsOfType().single()) } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index 5c4eb7fa2b..aa762d422d 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -68,8 +68,14 @@ data class LedgerTransaction( * If any contract fails to verify, the whole transaction is considered to be invalid. */ private fun verifyContracts() { - val contracts = (inputs.map { it.state.data.contract } + outputs.map { it.data.contract }).toSet() - for (contract in contracts) { + val contracts = (inputs.map { it.state.contract } + outputs.map { it.contract }).toSet() + 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 { contract.verify(this) } catch(e: Throwable) { diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 5e10965370..39ffd5ce87 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -7,6 +7,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachine import net.corda.core.node.ServiceHub import net.corda.core.node.services.KeyManagementService +import java.lang.UnsupportedOperationException import java.security.KeyPair import java.security.PublicKey import java.time.Duration @@ -58,7 +59,8 @@ open class TransactionBuilder( is StateAndRef<*> -> addInputState(t) is SecureHash -> addAttachment(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 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) @@ -99,14 +101,14 @@ open class TransactionBuilder( } @JvmOverloads - fun addOutputState(state: ContractState, notary: Party, encumbrance: Int? = null): TransactionBuilder { - return addOutputState(TransactionState(state, notary, encumbrance)) + fun addOutputState(state: ContractState, contract: ContractClassName, notary: Party, encumbrance: Int? = null): TransactionBuilder { + return addOutputState(TransactionState(state, contract, notary, encumbrance)) } /** 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" } - addOutputState(state, notary!!) + addOutputState(state, contract, notary!!) return this } diff --git a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt index e059dd2e1e..b960873a50 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt @@ -5,6 +5,8 @@ import net.corda.testing.contracts.DummyContractV2 import net.corda.core.crypto.SecureHash import net.corda.testing.ALICE 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 kotlin.test.assertEquals import kotlin.test.assertTrue @@ -16,18 +18,18 @@ class DummyContractV2Tests { @Test fun `upgrade from v1`() { 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 v1StateAndRef = StateAndRef(v1State, v1Ref) val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef) 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() assertEquals(expectedOutput, actualOutput) 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) } } diff --git a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt index 78e93f6b0e..871ec020de 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt @@ -6,6 +6,7 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.testing.DUMMY_NOTARY import net.corda.testing.TestDependencyInjectionBase +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockServices @@ -33,12 +34,10 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { private class StringTypeDummyState(val data: String) : ContractState { - override val contract: Contract = DummyContract() override val participants: List = emptyList() } private class IntTypeDummyState(val data: Int) : ContractState { - override val contract: Contract = DummyContract() override val participants: List = emptyList() } @@ -54,12 +53,12 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { val dummyState = makeDummyState(data) val fakeIssueTx = services.signInitialTransaction( TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(dummyState) + .addOutputState(dummyState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand()) ) services.recordTransactions(fakeIssueTx) 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 { @@ -67,8 +66,8 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { for (i in 0..4) { tx.addInputState(makeDummyStateAndRef(i)) tx.addInputState(makeDummyStateAndRef(i.toString())) - tx.addOutputState(makeDummyState(i)) - tx.addOutputState(makeDummyState(i.toString())) + tx.addOutputState(makeDummyState(i), DUMMY_PROGRAM_ID) + tx.addOutputState(makeDummyState(i.toString()), DUMMY_PROGRAM_ID) tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey)) tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey)) } diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt index 5da41dd686..65eac9056c 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt @@ -4,6 +4,7 @@ import net.corda.core.identity.AbstractParty import net.corda.core.transactions.LedgerTransaction import net.corda.finance.DOLLARS 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.testing.MEGA_CORP import net.corda.testing.MINI_CORP @@ -12,7 +13,7 @@ import org.junit.Test import java.time.Instant import java.time.temporal.ChronoUnit -val TEST_TIMELOCK_ID = TransactionEncumbranceTests.DummyTimeLock() +val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock" class TransactionEncumbranceTests { val defaultIssuer = MEGA_CORP.ref(1) @@ -40,7 +41,6 @@ class TransactionEncumbranceTests { val validFrom: Instant ) : ContractState { override val participants: List = emptyList() - override val contract: Contract = TEST_TIMELOCK_ID } } @@ -48,9 +48,9 @@ class TransactionEncumbranceTests { fun `state can be encumbered`() { ledger { transaction { - input { state } - output(encumbrance = 1) { stateWithNewOwner } - output("5pm time-lock") { timeLock } + input(CASH_PROGRAM_ID) { state } + output(CASH_PROGRAM_ID, encumbrance = 1) { stateWithNewOwner } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } verifies() } @@ -61,14 +61,14 @@ class TransactionEncumbranceTests { fun `state can transition if encumbrance rules are met`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } // Un-encumber the output if the time of the transaction is later than the timelock. transaction { input("state encumbered by 5pm time-lock") input("5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) verifies() @@ -80,14 +80,14 @@ class TransactionEncumbranceTests { fun `state cannot transition if the encumbrance contract fails to verify`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } // The time of the transaction is earlier than the time specified in the encumbering timelock. transaction { input("state encumbered by 5pm time-lock") input("5pm time-lock") - output { state } + output(CASH_PROGRAM_ID) { state } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FOUR_PM) 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`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock", encumbrance = 1) { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } transaction { input("state encumbered by 5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" @@ -116,8 +116,8 @@ class TransactionEncumbranceTests { fun `state cannot be encumbered by itself`() { ledger { transaction { - input { state } - output(encumbrance = 0) { stateWithNewOwner } + input(CASH_PROGRAM_ID) { state } + output(CASH_PROGRAM_ID, encumbrance = 0) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } this `fails with` "Missing required encumbrance 0 in OUTPUT" } @@ -128,9 +128,9 @@ class TransactionEncumbranceTests { fun `encumbrance state index must be valid`() { ledger { transaction { - input { state } - output(encumbrance = 2) { stateWithNewOwner } - output { timeLock } + input(CASH_PROGRAM_ID) { state } + output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner } + output(TEST_TIMELOCK_ID) { timeLock } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } this `fails with` "Missing required encumbrance 2 in OUTPUT" } @@ -141,14 +141,14 @@ class TransactionEncumbranceTests { fun `correct encumbrance state must be provided`() { ledger { unverifiedTransaction { - output("state encumbered by some other state", encumbrance = 1) { state } - output("some other state") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state } + output(CASH_PROGRAM_ID, "some other state") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } transaction { input("state encumbered by some other state") input("5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt index 169677b3d6..3d965aea46 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt @@ -8,6 +8,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import org.junit.Test import java.security.KeyPair @@ -93,7 +94,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test 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>() val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val commands = emptyList>() @@ -133,7 +134,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test fun `general transactions cannot change 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 inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0))) val outputs = listOf(outState) @@ -158,7 +159,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test 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( inputs = emptyList(), attachments = emptyList(), diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index 985d501bcf..4c14517faf 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -9,6 +9,7 @@ import net.corda.core.serialization.serialize import net.corda.core.transactions.WireTransaction import net.corda.finance.DOLLARS 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.testing.* import org.junit.Test @@ -31,13 +32,13 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { val testLedger = ledger { unverifiedTransaction { - output("MEGA_CORP cash") { + output(CASH_PROGRAM_ID, "MEGA_CORP cash") { Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MEGA_CORP ) } - output("dummy cash 1") { + output(CASH_PROGRAM_ID, "dummy cash 1") { Cash.State( amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MINI_CORP @@ -47,7 +48,7 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") - output("MEGA_CORP cash".output().copy(owner = MINI_CORP)) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = MINI_CORP)) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } timeWindow(TEST_TX_TIME) this.verifies() diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index cf278dc307..af17e33e6b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -2,6 +2,7 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.requireThat import net.corda.core.identity.AnonymousParty 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.unwrap 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.node.MockNetwork import net.corda.testing.node.MockServices @@ -88,7 +90,7 @@ class CollectSignaturesFlowTests { val myInputKeys = state.participants.map { it.owningKey } val myKeys = myInputKeys + (identities[serviceHub.myInfo.legalIdentity] ?: serviceHub.myInfo.legalIdentity).owningKey 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 stx = subFlow(CollectSignaturesFlow(ptx, myKeys)) val ftx = subFlow(FinalityFlow(stx)).single() @@ -109,7 +111,7 @@ class CollectSignaturesFlowTests { val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val myInputKeys = state.participants.map { it.owningKey } 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 stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys)) val ftx = subFlow(FinalityFlow(stx)).single() diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index b778b23f04..db69d2981b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -14,6 +14,7 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.USD 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.flows.CashIssueFlow import net.corda.node.internal.CordaRPCOpsImpl @@ -234,13 +235,14 @@ class ContractUpgradeFlowTest { assertEquals>(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 { - override val legacyContract = Cash::class.java + override val legacyContract = CASH_PROGRAM_ID data class State(override val amount: Amount>, val owners: List) : FungibleAsset { override val owner: AbstractParty = owners.first() override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet() - override val contract = CashV2() override val participants = owners override fun withNewOwnerAndAmount(newAmount: Amount>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index b5876f52a9..fc5872ce18 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -12,6 +12,7 @@ import kotlin.test.assertFailsWith class VaultUpdateTests { + val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract" object DummyContract : Contract { @@ -20,9 +21,7 @@ class VaultUpdateTests { } private class DummyState : ContractState { - override val participants: List - get() = emptyList() - override val contract = VaultUpdateTests.DummyContract + override val participants: List = emptyList() } private val stateRef0 = StateRef(SecureHash.randomSHA256(), 0) @@ -31,11 +30,11 @@ class VaultUpdateTests { private val stateRef3 = StateRef(SecureHash.randomSHA256(), 3) private val stateRef4 = StateRef(SecureHash.randomSHA256(), 4) - private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef0) - private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef1) - private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef2) - private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef3) - private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef4) + private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef0) + private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef1) + private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef2) + private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef3) + private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef4) @Test fun `nothing plus nothing is nothing`() { diff --git a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt index 20bf1b8f88..244db2eddc 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt @@ -15,9 +15,9 @@ import java.util.* import kotlin.test.assertEquals import kotlin.test.assertFailsWith -val TEST_PROGRAM_ID = TransactionSerializationTests.TestCash() - class TransactionSerializationTests : TestDependencyInjectionBase() { + val TEST_CASH_PROGRAM_ID = "net.corda.core.serialization.TransactionSerializationTests.TestCash" + class TestCash : Contract { override fun verify(tx: LedgerTransaction) { } @@ -26,7 +26,6 @@ class TransactionSerializationTests : TestDependencyInjectionBase() { val deposit: PartyAndReference, val amount: Amount, override val owner: AbstractParty) : OwnableState { - override val contract: Contract = TEST_PROGRAM_ID override val participants: List get() = listOf(owner) @@ -42,9 +41,9 @@ class TransactionSerializationTests : TestDependencyInjectionBase() { // It refers to a fake TX/state that we don't bother creating here. val depositRef = MINI_CORP.ref(1) val fakeStateRef = generateStateRef() - val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef) - val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY) - val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY) + 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), TEST_CASH_PROGRAM_ID, 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 notaryServices = MockServices(DUMMY_NOTARY_KEY) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 0f66efa1de..27dee20a3f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,9 @@ from the previous milestone release. 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``, and the test scope modules are now located in a ``testing`` directory. diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index 998b6bb4d9..a7caceebba 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -22,6 +22,7 @@ import net.corda.core.utilities.UntrustworthyData; import net.corda.core.utilities.X500NameUtils; import net.corda.finance.contracts.asset.Cash; import net.corda.testing.contracts.DummyContract; +import net.corda.testing.contracts.DummyContractKt; import net.corda.testing.contracts.DummyState; import org.jetbrains.annotations.NotNull; @@ -331,7 +332,7 @@ public class FlowCookbookJava { // We can also add items using methods for the individual components: // DOCSTART 28 txBuilder.addInputState(ourStateAndRef); - txBuilder.addOutputState(ourOutput); + txBuilder.addOutputState(ourOutput, DummyContractKt.getDUMMY_PROGRAM_ID()); txBuilder.addCommand(ourCommand); txBuilder.addAttachment(ourAttachment); // DOCEND 28 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index 1824302eb6..daff7ec006 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -20,6 +20,7 @@ import net.corda.core.utilities.* import net.corda.core.utilities.ProgressTracker.Step import net.corda.finance.contracts.asset.Cash 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.DummyState import java.security.PublicKey @@ -309,7 +310,7 @@ object FlowCookbook { // We can also add items using methods for the individual components: // DOCSTART 28 txBuilder.addInputState(ourStateAndRef) - txBuilder.addOutputState(ourOutput) + txBuilder.addOutputState(ourOutput, DUMMY_PROGRAM_ID) txBuilder.addCommand(ourCommand) txBuilder.addAttachment(ourAttachment) // DOCEND 28 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 13d6b13acc..6803b665de 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -1,10 +1,7 @@ package net.corda.docs import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.withoutIssuer +import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature 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.TransactionBuilder 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.schemas.CashSchemaV1 import java.util.* @@ -184,8 +182,8 @@ class ForeignExchangeFlow(val tradeId: String, // Build and add the inputs and outputs builder.withItems(*ourInputStates.toTypedArray()) builder.withItems(*theirInputStates.toTypedArray()) - builder.withItems(*ourOutputState.toTypedArray()) - builder.withItems(*theirOutputState.toTypedArray()) + builder.withItems(*ourOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray()) + builder.withItems(*theirOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray()) // We have already validated their response and trust our own data // so we can sign. Note the returned SignedTransaction is still not fully signed diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index 8d42157936..be68e11fe5 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -26,6 +26,8 @@ enum class WorkflowState { 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. * 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 counterparty: Party, val state: WorkflowState = WorkflowState.NEW, - override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId), - override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState { - + override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId)) : LinearState { val parties: List get() = listOf(source, counterparty) override val participants: List get() = parties } @@ -108,7 +108,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, val notary = serviceHub.networkMapCache.getAnyNotary() // Create the TransactionBuilder and populate with the new state. 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) // We can automatically sign as there is no untrusted data. val signedTx = serviceHub.signInitialTransaction(tx) @@ -168,7 +168,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow val tx = TransactionBuilder(notary). withItems( latestRecord, - newState, + StateAndContract(newState, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Completed(), listOf(serviceHub.myInfo.legalIdentity.owningKey, latestRecord.state.data.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) diff --git a/docs/source/hello-world-state.rst b/docs/source/hello-world-state.rst index a4ba0ef205..1677e3276a 100644 --- a/docs/source/hello-world-state.rst +++ b/docs/source/hello-world-state.rst @@ -74,7 +74,7 @@ define an ``IOUState``: class IOUState(val value: Int, val lender: Party, val borrower: Party) : ContractState { - override val contract = TemplateContract() + override val contract = "net.corda.contract.TemplateContract" override val participants get() = listOf(lender, borrower) } diff --git a/docs/source/tutorial-contract.rst b/docs/source/tutorial-contract.rst index 3768c34fc9..ffefcfb2fa 100644 --- a/docs/source/tutorial-contract.rst +++ b/docs/source/tutorial-contract.rst @@ -102,7 +102,7 @@ A state is a class that stores data that is checked by the contract. A commercia val faceValue: Amount>, val maturityDate: Instant ) : OwnableState { - override val contract = CommercialPaper() + override val contract = "net.corda.finance.contracts.CommercialPaper" override val participants = listOf(owner) fun withoutOwner() = copy(owner = AnonymousParty(NullPublicKey)) diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt index d438326e7b..275d24a54d 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt @@ -10,13 +10,11 @@ import net.corda.finance.contracts.FixOf import java.math.BigDecimal import java.time.Instant -val UNIVERSAL_PROGRAM_ID = UniversalContract() +val UNIVERSAL_PROGRAM_ID = "net.corda.finance.contracts.universal.UniversalContract" class UniversalContract : Contract { data class State(override val participants: List, - val details: Arrangement) : ContractState { - override val contract = UNIVERSAL_PROGRAM_ID - } + val details: Arrangement) : ContractState interface Commands : CommandData { @@ -317,7 +315,7 @@ class UniversalContract : Contract { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) { 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) } } diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt index 91740eb915..11e67b0ef4 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt @@ -166,7 +166,7 @@ class Cap { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { @@ -183,8 +183,8 @@ class Cap { @Test fun `first fixing`() { transaction { - input { stateInitial } - output { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } timeWindow(TEST_TX_TIME_1) tweak { @@ -228,9 +228,9 @@ class Cap { @Test fun `first execute`() { transaction { - input { stateAfterFixingFirst } - output { stateAfterExecutionFirst } - output { statePaymentFirst } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } timeWindow(TEST_TX_TIME_1) @@ -248,8 +248,8 @@ class Cap { @Test fun `final execute`() { transaction { - input { stateAfterFixingFinal } - output { statePaymentFinal } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal } timeWindow(TEST_TX_TIME_1) @@ -267,8 +267,8 @@ class Cap { @Test fun `second fixing`() { transaction { - input { stateAfterExecutionFirst } - output { stateAfterFixingFinal } + input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt index 908ef49061..8f6c2c61dc 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt @@ -53,7 +53,7 @@ class Caplet { @Test fun issue() { transaction { - output { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStart } timeWindow(TEST_TX_TIME_1) tweak { @@ -70,8 +70,8 @@ class Caplet { @Test fun `execute`() { transaction { - input { stateFixed } - output { stateFinal } + input(UNIVERSAL_PROGRAM_ID) { stateFixed } + output(UNIVERSAL_PROGRAM_ID) { stateFinal } timeWindow(TEST_TX_TIME_1) tweak { @@ -88,8 +88,8 @@ class Caplet { @Test fun `fixing`() { transaction { - input { stateStart } - output { stateFixed } + input(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateFixed } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt index 4e76908c77..31bd7fbc1b 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt @@ -50,7 +50,7 @@ class FXFwdTimeOption @Test fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } timeWindow(TEST_TX_TIME_1) tweak { @@ -71,9 +71,9 @@ class FXFwdTimeOption @Test fun `maturity, bank exercise`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_AFTER_MATURITY) @@ -103,9 +103,9 @@ class FXFwdTimeOption @Test fun `maturity, corp exercise`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_BEFORE_MATURITY) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt index a34239017e..b4abe74eef 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt @@ -42,7 +42,7 @@ class FXSwap { fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } timeWindow(TEST_TX_TIME_1) tweak { @@ -63,9 +63,9 @@ class FXSwap { @Test fun `execute`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) tweak { @@ -82,9 +82,9 @@ class FXSwap { @Test fun `execute - reversed order`() { transaction { - input { inState } - output { outState2 } - output { outState1 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState2 } + output(UNIVERSAL_PROGRAM_ID) { outState1 } timeWindow(TEST_TX_TIME_1) tweak { @@ -101,9 +101,9 @@ class FXSwap { @Test fun `execute - not authorized`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } @@ -114,9 +114,9 @@ class FXSwap { @Test fun `execute - before maturity`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_TOO_EARLY) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -127,8 +127,8 @@ class FXSwap { @Test fun `execute - outState mismatch 1`() { transaction { - input { inState } - output { outState1 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -139,9 +139,9 @@ class FXSwap { @Test fun `execute - outState mismatch 2`() { transaction { - input { inState } - output { outState1 } - output { outStateBad2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outStateBad2 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -152,9 +152,9 @@ class FXSwap { @Test fun `execute - outState mismatch 3`() { transaction { - input { inState } - output { outStateBad1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outStateBad1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -165,9 +165,9 @@ class FXSwap { @Test fun `execute - outState mismatch 4`() { transaction { - input { inState } - output { outState1 } - output { outStateBad3 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outStateBad3 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt index 9427a9f087..8983e60e93 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt @@ -133,7 +133,7 @@ class IRS { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { @@ -150,8 +150,8 @@ class IRS { @Test fun `first fixing`() { transaction { - input { stateInitial } - output { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } timeWindow(TEST_TX_TIME_1) tweak { @@ -195,9 +195,9 @@ class IRS { @Test fun `first execute`() { transaction { - input { stateAfterFixingFirst } - output { stateAfterExecutionFirst } - output { statePaymentFirst } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } timeWindow(TEST_TX_TIME_1) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt index 31d18cd810..03d8a14eea 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt @@ -142,7 +142,7 @@ class RollOutTests { @Test fun issue() { transaction { - output { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStart } timeWindow(TEST_TX_TIME_1) tweak { @@ -159,9 +159,9 @@ class RollOutTests { @Test fun `execute`() { transaction { - input { stateStart } - output { stateStep1a } - output { stateStep1b } + input(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStep1a } + output(UNIVERSAL_PROGRAM_ID) { stateStep1b } timeWindow(TEST_TX_TIME_1) /* tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt index 270e807f84..34dcdc6f64 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt @@ -59,7 +59,7 @@ class Swaption { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt index c208128996..fdce06b22b 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt @@ -49,7 +49,7 @@ class ZeroCouponBond { @Test fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } tweak { command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } @@ -65,8 +65,8 @@ class ZeroCouponBond { @Test fun `execute`() { transaction { - input { inState } - output { outState } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState } timeWindow(TEST_TX_TIME_1) tweak { @@ -83,8 +83,8 @@ class ZeroCouponBond { @Test fun `execute - not authorized`() { transaction { - input { inState } - output { outState } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState } timeWindow(TEST_TX_TIME_1) command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } @@ -95,8 +95,8 @@ class ZeroCouponBond { @Test fun `execute - outState mismatch`() { transaction { - input { inState } - output { outStateWrong } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outStateWrong } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -107,10 +107,10 @@ class ZeroCouponBond { @Test fun move() { transaction { - input { inState } + input(UNIVERSAL_PROGRAM_ID) { inState } tweak { - output { outStateMove } + output(UNIVERSAL_PROGRAM_ID) { outStateMove } command(acmeCorp.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) } @@ -118,14 +118,14 @@ class ZeroCouponBond { } tweak { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) } this `fails with` "output state does not reflect move command" } - output { outStateMove } + output(UNIVERSAL_PROGRAM_ID) { outStateMove } command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) diff --git a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt index 9e1d5bf6a2..a8b4103649 100644 --- a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt +++ b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt @@ -7,11 +7,12 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder 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 { + val magicString = "helloworld" + data class State(val magicNumber: Int = 0) : ContractState { - override val contract = ANOTHER_DUMMY_PROGRAM_ID override val participants: List get() = emptyList() } @@ -26,7 +27,7 @@ class AnotherDummyContract : Contract, DummyContractBackdoor { override fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { 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 diff --git a/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java b/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java index 0f2d081043..3838fb4feb 100644 --- a/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java +++ b/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java @@ -31,7 +31,7 @@ import static net.corda.core.contracts.ContractsDSL.requireThat; */ @SuppressWarnings("unused") 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") public static class State implements OwnableState, ICommercialPaperState { @@ -90,12 +90,6 @@ public class JavaCommercialPaper implements Contract { return maturityDate; } - @NotNull - @Override - public Contract getContract() { - return JCP_PROGRAM_ID; - } - @Override public boolean equals(Object that) { if (this == that) return true; @@ -236,7 +230,7 @@ public class JavaCommercialPaper implements Contract { public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) { 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())); } @@ -253,7 +247,7 @@ public class JavaCommercialPaper implements Contract { public void generateMove(TransactionBuilder tx, StateAndRef paper, AbstractParty newOwner) { 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())); } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt index 8626da9034..4953a56f25 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt @@ -43,7 +43,7 @@ import java.util.* // TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance. class CommercialPaper : Contract { companion object { - val CP_PROGRAM_ID = CommercialPaper() + val CP_PROGRAM_ID = "net.corda.finance.contracts.CommercialPaper" } data class State( val issuance: PartyAndReference, @@ -51,7 +51,6 @@ class CommercialPaper : Contract { val faceValue: Amount>, val maturityDate: Instant ) : OwnableState, QueryableState, ICommercialPaperState { - override val contract = CP_PROGRAM_ID override val participants = listOf(owner) 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 `with notary`(notary: Party) = TransactionState(this, notary) } interface Commands : CommandData { @@ -164,7 +162,7 @@ class CommercialPaper : Contract { fun generateIssue(issuance: PartyAndReference, faceValue: Amount>, maturityDate: Instant, notary: Party): TransactionBuilder { 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, newOwner: AbstractParty) { 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) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index 366b5ee666..69b2c937f2 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -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. -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 @@ -123,7 +123,6 @@ class Cash : OnLedgerAsset() { : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey) - override val contract = CASH_PROGRAM_ID override val participants = listOf(owner) override fun withNewOwnerAndAmount(newAmount: Amount>, newOwner: AbstractParty): FungibleAsset @@ -191,7 +190,7 @@ class Cash : OnLedgerAsset() { * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. */ fun generateIssue(tx: TransactionBuilder, amount: Amount>, 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, amount: Amount>, owner: AbstractParty) = txState.copy(data = txState.data.copy(amount = amount, owner = owner)) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt index dac289882d..2e3bbfaeb1 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt @@ -33,7 +33,7 @@ import java.util.* class CommodityContract : OnLedgerAsset() { companion object { // 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 */ @@ -45,8 +45,6 @@ class CommodityContract : OnLedgerAsset { constructor(deposit: PartyAndReference, amount: Amount, owner: AbstractParty) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) - - override val contract = COMMODITY_PROGRAM_ID override val exitKeys: Set = Collections.singleton(owner.owningKey) override val participants = listOf(owner) @@ -160,7 +158,7 @@ class CommodityContract : OnLedgerAsset>, 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, amount: Amount>, owner: AbstractParty) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt index b7968bc755..27548a0782 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt @@ -64,7 +64,7 @@ data class MultilateralNetState

( // 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() +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 @@ -134,7 +134,6 @@ class Obligation

: Contract { val beneficiary: AbstractParty ) : FungibleAsset>, NettableState, MultilateralNetState

> { override val amount: Amount>> = Amount(quantity, Issued(obligor.ref(0), template)) - override val contract = OBLIGATION_PROGRAM_ID override val exitKeys: Collection = setOf(beneficiary.owningKey) val dueBefore: Instant = template.dueBefore override val participants: List = listOf(obligor, beneficiary) @@ -491,7 +490,7 @@ class Obligation

: Contract { tx.withItems(*inputs) val out = states.reduce(State

::net) if (out.quantity > 0L) - tx.addOutputState(out) + tx.addOutputState(out, OBLIGATION_PROGRAM_ID) tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey) } @@ -534,7 +533,7 @@ class Obligation

: Contract { beneficiary: AbstractParty, notary: Party) { 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

: Contract { pennies: Long, beneficiary: AbstractParty, 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, issued: Issued>, @@ -587,7 +586,7 @@ class Obligation

: Contract { netState.template, entry.value.quantity, entry.key.second) } // 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 }) } @@ -618,7 +617,7 @@ class Obligation

: Contract { stateAndRefs.forEach { stateAndRef -> val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) tx.addInputState(stateAndRef) - tx.addOutputState(outState, notary) + tx.addOutputState(outState, OBLIGATION_PROGRAM_ID, notary) partiesUsed.add(stateAndRef.state.data.beneficiary) } tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct()) @@ -671,13 +670,13 @@ class Obligation

: Contract { val assetState = ref.state.data val amount = Amount(assetState.amount.quantity, assetState.amount.token.product) if (obligationRemaining >= amount) { - tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), OBLIGATION_PROGRAM_ID, notary) obligationRemaining -= amount } else { val change = Amount(obligationRemaining.quantity, assetState.amount.token) // Split the state in two, sending the change back to the previous beneficiary - tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), notary) - tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), OBLIGATION_PROGRAM_ID, notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), OBLIGATION_PROGRAM_ID, notary) obligationRemaining -= Amount(0L, obligationRemaining.token) } assetSigners.add(assetState.owner) @@ -686,7 +685,7 @@ class Obligation

: Contract { // If we haven't cleared the full obligation, add the remainder as an output 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 { // Destroy all of the states } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index bac6013f74..6d49e057b2 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -224,7 +224,7 @@ object TwoPartyTradeFlow { tx.addInputState(assetForSale) 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) // We set the transaction's time-window: it may be that none of the contracts need this! diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index fabf51ef34..a84067082c 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -19,41 +19,42 @@ public class CashTestsJava { 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 outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY())); + private final String CASH_PROGRAM_ID = CashUtilities.getCASH_PROGRAM_ID(); @Test public void trivial() { transaction(tx -> { - tx.input(inState); + tx.input(CASH_PROGRAM_ID, inState); 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()); return tw.failsWith("the amounts balance"); }); tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE); // Invalid command return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command"); }); tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.failsWith("the owning keys are a subset of the signing keys"); }); 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 // 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()); return tw.failsWith("at least one cash input"); }); // Simple reallocation works. return tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.verifies(); }); diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index e72638806f..104c7d802a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -31,6 +31,7 @@ interface ICommercialPaperTestTemplate { fun getIssueCommand(notary: Party): CommandData fun getRedeemCommand(notary: Party): CommandData fun getMoveCommand(): CommandData + fun getContract(): ContractClassName } class JavaCommercialPaperTest : ICommercialPaperTestTemplate { @@ -44,6 +45,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move() + override fun getContract() = JavaCommercialPaper.JCP_PROGRAM_ID } class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { @@ -57,6 +59,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() + override fun getContract() = CommercialPaper.CP_PROGRAM_ID } class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { @@ -70,6 +73,7 @@ class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() + override fun getContract() = CommercialPaper.CP_PROGRAM_ID } @RunWith(Parameterized::class) @@ -89,13 +93,13 @@ class CommercialPaperTestsGeneric { val someProfits = 1200.DOLLARS `issued by` issuer ledger { unverifiedTransaction { - output("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, "alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE) + output(CASH_PROGRAM_ID, "some profits", someProfits.STATE `owned by` MEGA_CORP) } // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { - output("paper") { thisTest.getPaper() } + output(thisTest.getContract(), "paper") { thisTest.getPaper() } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this.verifies() @@ -106,8 +110,8 @@ class CommercialPaperTestsGeneric { transaction("Trade") { input("paper") input("alice's $900") - output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP } - output("alice's paper") { "paper".output().withOwner(ALICE) } + output(CASH_PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP } + output(thisTest.getContract(), "alice's paper") { "paper".output().withOwner(ALICE) } command(ALICE_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } this.verifies() @@ -120,8 +124,8 @@ class CommercialPaperTestsGeneric { input("some profits") fun TransactionDSL.outputs(aliceGetsBack: Amount>) { - output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE } - output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP } + output(CASH_PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE `owned by` ALICE } + output(CASH_PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP } } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } @@ -142,7 +146,7 @@ class CommercialPaperTestsGeneric { timeWindow(TEST_TX_TIME + 8.days) tweak { - output { "paper".output() } + output(thisTest.getContract()) { "paper".output() } this `fails with` "must be destroyed" } @@ -154,7 +158,7 @@ class CommercialPaperTestsGeneric { @Test fun `key mismatch at issue`() { transaction { - output { thisTest.getPaper() } + output(thisTest.getContract()) { thisTest.getPaper() } command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "output states are issued by a command signer" @@ -164,7 +168,7 @@ class CommercialPaperTestsGeneric { @Test fun `face value is not zero`() { 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) } timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" @@ -174,7 +178,7 @@ class CommercialPaperTestsGeneric { @Test fun `maturity date not in the past`() { 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) } timeWindow(TEST_TX_TIME) this `fails with` "maturity date is not in the past" @@ -184,8 +188,8 @@ class CommercialPaperTestsGeneric { @Test fun `issue cannot replace an existing state`() { transaction { - input(thisTest.getPaper()) - output { thisTest.getPaper() } + input(thisTest.getContract(), thisTest.getPaper()) + output(thisTest.getContract()) { thisTest.getPaper() } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 4d7843f707..410d8a4823 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -84,33 +84,33 @@ class CashTests : TestDependencyInjectionBase() { @Test fun trivial() { transaction { - input { inState } + input(CASH_PROGRAM_ID) { inState } 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() } this `fails with` "the amounts balance" } tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { DummyCommandData } // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" } tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(BOB_PUBKEY) { Cash.Commands.Move() } this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output { outState } - output { outState `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { outState } + output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "at least one cash input" } // Simple reallocation works. tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Move() } this.verifies() } @@ -121,8 +121,8 @@ class CashTests : TestDependencyInjectionBase() { fun `issue by move`() { // Check we can't "move" money into existence. transaction { - input { DummyState() } - output { outState } + input(CASH_PROGRAM_ID) { DummyState() } + output(CASH_PROGRAM_ID) { outState } command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } 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 // institution is allowed to issue as much cash as they want. transaction { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Issue() } this `fails with` "output states are issued by a command signer" } transaction { - output { + output(CASH_PROGRAM_ID) { Cash.State( amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(ALICE_PUBKEY) @@ -182,8 +182,8 @@ class CashTests : TestDependencyInjectionBase() { fun `extended issue examples`() { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { - input { issuerInState } - output { inState.copy(amount = inState.amount * 2) } + input(CASH_PROGRAM_ID) { issuerInState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } // Move fails: not allowed to summon money. tweak { @@ -200,24 +200,24 @@ class CashTests : TestDependencyInjectionBase() { // Can't use an issue command to lower the amount. transaction { - input { inState } - output { inState.copy(amount = inState.amount.splitEvenly(2).first()) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { - input { inState } - output { inState } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } 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) transaction { - input { inState } - output { inState.copy(amount = inState.amount * 2) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } tweak { command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } @@ -252,24 +252,24 @@ class CashTests : TestDependencyInjectionBase() { transaction { command(ALICE_PUBKEY) { Cash.Commands.Move() } tweak { - input { inState } + input(CASH_PROGRAM_ID) { inState } 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() } // Merging 4 inputs into 2 outputs works. tweak { val splits2 = inState.amount.splitEvenly(2) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) input { inState.copy(amount = splits4[i]) } - for (i in 0..1) output { inState.copy(amount = splits2[i]) } + for (i in 0..3) input(CASH_PROGRAM_ID) { inState.copy(amount = splits4[i]) } + for (i in 0..1) output(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) } this.verifies() } // Merging 2 inputs into 1 works. tweak { val splits2 = inState.amount.splitEvenly(2) - for (i in 0..1) input { inState.copy(amount = splits2[i]) } - output { inState } + for (i in 0..1) input(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) } + output(CASH_PROGRAM_ID) { inState } this.verifies() } } @@ -278,15 +278,15 @@ class CashTests : TestDependencyInjectionBase() { @Test fun zeroSizedValues() { transaction { - input { inState } - input { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "zero sized inputs" } transaction { - input { inState } - output { inState } - output { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "zero sized outputs" } @@ -296,52 +296,52 @@ class CashTests : TestDependencyInjectionBase() { fun trivialMismatches() { // Can't change issuer. transaction { - input { inState } - output { outState `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't change deposit reference when splitting. transaction { val splits2 = inState.amount.splitEvenly(2) - input { inState } - for (i in 0..1) output { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } + input(CASH_PROGRAM_ID) { inState } + for (i in 0..1) output(CASH_PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't mix currencies. transaction { - input { inState } - output { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } - output { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } + output(CASH_PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } transaction { - input { inState } - input { + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.copy( amount = 150.POUNDS `issued by` defaultIssuer, 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() } this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { - input { inState } - input { inState `issued by` MINI_CORP } - output { outState } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't combine two different deposits at the same issuer. transaction { - input { inState } - input { inState.editDepositRef(3) } - output { outState.copy(amount = inState.amount * 2).editDepositRef(3) } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.editDepositRef(3) } + output(CASH_PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "for reference [01]" } @@ -351,8 +351,8 @@ class CashTests : TestDependencyInjectionBase() { fun exitLedger() { // Single input/output straightforward case. transaction { - input { issuerInState } - output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + input(CASH_PROGRAM_ID) { issuerInState } + output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } tweak { 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`() { // Multi-issuer case. transaction { - input { issuerInState } - input { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { issuerInState } + 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 { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP } + 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() } @@ -398,8 +398,8 @@ class CashTests : TestDependencyInjectionBase() { fun `exit cash not held by its issuer`() { // Single input/output straightforward case. transaction { - input { inState } - output { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + input(CASH_PROGRAM_ID) { inState } + 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(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" @@ -410,25 +410,25 @@ class CashTests : TestDependencyInjectionBase() { fun multiIssuer() { transaction { // Gather 2000 dollars from two different issuers. - input { inState } - input { inState `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } // Can't merge them together. 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" } // Missing MiniCorp deposit tweak { - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } this `fails with` "the amounts balance" } // This works. - output { 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)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } this.verifies() } } @@ -438,10 +438,10 @@ class CashTests : TestDependencyInjectionBase() { // Check we can do an atomic currency trade tx. transaction { 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 { pounds } - output { inState `owned by` AnonymousParty(BOB_PUBKEY) } - output { pounds `owned by` AnonymousParty(ALICE_PUBKEY) } + input(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(ALICE_PUBKEY) } + input(CASH_PROGRAM_ID) { pounds } + output(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } + output(CASH_PROGRAM_ID) { pounds `owned by` AnonymousParty(ALICE_PUBKEY) } command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } this.verifies() @@ -460,7 +460,7 @@ class CashTests : TestDependencyInjectionBase() { fun makeCash(amount: Amount, corp: Party, depositRef: Byte = 1) = StateAndRef( - Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1) `with notary` DUMMY_NOTARY, + TransactionState(Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1), CASH_PROGRAM_ID, DUMMY_NOTARY), StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) ) @@ -753,7 +753,7 @@ class CashTests : TestDependencyInjectionBase() { fun chainCashDoubleSpendFailsWith() { ledger { unverifiedTransaction { - output("MEGA_CORP cash") { + output(CASH_PROGRAM_ID, "MEGA_CORP cash") { Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MEGA_CORP @@ -763,7 +763,7 @@ class CashTests : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") - output("MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } this.verifies() } @@ -772,7 +772,7 @@ class CashTests : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") // We send it to another pubkey so that the transaction is not identical to the previous one - output("MEGA_CORP cash".output().copy(owner = ALICE)) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = ALICE)) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } this.verifies() } diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt index be5ff9a189..da55397e23 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt @@ -30,7 +30,6 @@ class DummyFungibleContract : OnLedgerAsset>, newOwner: AbstractParty): FungibleAsset diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 7bd7fbe4e6..85a4b64a7a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -16,6 +16,7 @@ import net.corda.finance.contracts.Commodity import net.corda.finance.contracts.NetType import net.corda.finance.contracts.asset.Obligation.Lifecycle import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices import org.junit.After @@ -54,10 +55,10 @@ class ObligationTests { group: LedgerDSL ) = group.apply { unverifiedTransaction { - output("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("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 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + output(OBLIGATION_PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB)) + 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 fun trivial() { transaction { - input { inState } + input(OBLIGATION_PROGRAM_ID) { inState } tweak { - output { outState.copy(quantity = 2000.DOLLARS.quantity) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { DummyCommandData } // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" } tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(BOB_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output { outState } - output { outState `issued by` MINI_CORP } + output(OBLIGATION_PROGRAM_ID) { outState } + output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "at least one obligation input" } // Simple reallocation works. tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this.verifies() } @@ -106,8 +107,8 @@ class ObligationTests { fun `issue debt`() { // Check we can't "move" debt into existence. transaction { - input { DummyState() } - output { outState } + input(DUMMY_PROGRAM_ID) { DummyState() } + output(OBLIGATION_PROGRAM_ID) { outState } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } 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 // institution is allowed to issue as much cash as they want. transaction { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Issue() } this `fails with` "output states are issued by a command signer" } transaction { - output { + output(OBLIGATION_PROGRAM_ID) { Obligation.State( obligor = MINI_CORP, 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. transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity * 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } // Move fails: not allowed to summon money. tweak { @@ -171,24 +172,24 @@ class ObligationTests { // Can't use an issue command to lower the amount. transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity / 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { - input { inState } - output { inState } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } this `fails with` "" } // Can't have any other commands if we have an issue command (because the issue command overrules them). transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity * 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } tweak { command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } @@ -223,8 +224,8 @@ class ObligationTests { @Test fun `generate close-out net transaction`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) + val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -235,8 +236,8 @@ class ObligationTests { @Test fun `generate close-out net transaction with remainder`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + 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), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -250,8 +251,8 @@ class ObligationTests { @Test fun `generate payment net transaction`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) + val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generatePaymentNetting(this, obligationAliceToBob.state.data.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -262,9 +263,9 @@ class ObligationTests { @Test fun `generate payment net transaction with remainder`() { 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 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 tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generatePaymentNetting(this, obligationAliceToBobState.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) @@ -275,8 +276,8 @@ class ObligationTests { assertEquals(expected, actual) } - private inline fun getStateAndRef(state: T): StateAndRef { - val txState = TransactionState(state, DUMMY_NOTARY) + private inline fun getStateAndRef(state: T, contractClassName: ContractClassName): StateAndRef { + val txState = TransactionState(state, contractClassName, DUMMY_NOTARY) return StateAndRef(txState, StateRef(SecureHash.randomSHA256(), 0)) } @@ -366,7 +367,7 @@ class ObligationTests { input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") 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) } timeWindow(TEST_TX_TIME) this.verifies() @@ -380,7 +381,7 @@ class ObligationTests { transaction("Issuance") { input("Alice's $1,000,000 obligation to Bob") 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) } timeWindow(TEST_TX_TIME) this `fails with` "amounts owed on input and output must match" @@ -434,7 +435,7 @@ class ObligationTests { transaction("Issuance") { input("Bob's $1,000,000 obligation to Alice") 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) } timeWindow(TEST_TX_TIME) this.verifies() @@ -464,7 +465,7 @@ class ObligationTests { transaction("Settlement") { input("Alice's $1,000,000 obligation to Bob") 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) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -476,10 +477,10 @@ class ObligationTests { val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer ledger { transaction("Settlement") { - input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) - output("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 } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + input(CASH_PROGRAM_ID, 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) + output(OBLIGATION_PROGRAM_ID, "Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, 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) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -491,9 +492,9 @@ class ObligationTests { val defaultedObligation: Obligation.State = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) ledger { transaction("Settlement") { - input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob - input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) - output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } + input(OBLIGATION_PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob + input(CASH_PROGRAM_ID, 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) + 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) { Cash.Commands.Move(Obligation::class.java) } this `fails with` "all inputs are in the normal state" @@ -506,7 +507,7 @@ class ObligationTests { transaction("Settlement") { input("Alice's $1,000,000 obligation to Bob") 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) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -526,13 +527,13 @@ class ObligationTests { // Try settling a simple commodity obligation ledger { unverifiedTransaction { - output("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 obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB)) + output(OBLIGATION_PROGRAM_ID, "Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE)) } transaction("Settlement") { input("Alice's 1 FCOJ obligation to Bob") 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) { CommodityContract.Commands.Move(Obligation::class.java) } attachment(attachment(commodityContractBytes.inputStream())) @@ -548,7 +549,7 @@ class ObligationTests { cashObligationTestRoots(this) transaction("Settlement") { 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) } 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 futureTestTime = TEST_TX_TIME + 7.days transaction("Settlement") { - input(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) } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) + 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) } timeWindow(TEST_TX_TIME) this `fails with` "the due date has passed" @@ -568,8 +569,8 @@ class ObligationTests { // Try defaulting an obligation that is now in the past ledger { transaction("Settlement") { - input(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) } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) + 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) } timeWindow(TEST_TX_TIME) this.verifies() @@ -584,22 +585,22 @@ class ObligationTests { transaction { command(CHARLIE.owningKey) { Obligation.Commands.Move() } tweak { - input { inState } - repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } } + input(OBLIGATION_PROGRAM_ID) { inState } + repeat(4) { output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { - repeat(4) { input { inState.copy(quantity = inState.quantity / 4) } } - output { inState.copy(quantity = inState.quantity / 2) } - output { inState.copy(quantity = inState.quantity / 2) } + repeat(4) { input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } this.verifies() } // Merging 2 inputs into 1 works. tweak { - input { inState.copy(quantity = inState.quantity / 2) } - input { inState.copy(quantity = inState.quantity / 2) } - output { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + output(OBLIGATION_PROGRAM_ID) { inState } this.verifies() } } @@ -610,15 +611,15 @@ class ObligationTests { transaction { command(CHARLIE.owningKey) { Obligation.Commands.Move() } tweak { - input { inState } - input { inState.copy(quantity = 0L) } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) } this `fails with` "zero sized inputs" } tweak { - input { inState } - output { inState } - output { inState.copy(quantity = 0L) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) } this `fails with` "zero sized outputs" } @@ -629,37 +630,37 @@ class ObligationTests { fun trivialMismatches() { // Can't change issuer. transaction { - input { inState } - output { outState `issued by` MINI_CORP } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // Can't mix currencies. transaction { - input { inState } - output { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } - output { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } transaction { - input { inState } - input { + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy( quantity = 15000, template = megaCorpPoundSettlement, beneficiary = AnonymousParty(BOB_PUBKEY) ) } - output { outState.copy(quantity = 115000) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 115000) } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { - input { inState } - input { inState `issued by` MINI_CORP } - output { outState } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } @@ -669,8 +670,8 @@ class ObligationTests { fun `exit single product obligation`() { // Single input/output straightforward case. transaction { - input { inState } - output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } tweak { command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } @@ -695,11 +696,11 @@ class ObligationTests { fun `exit multiple product obligations`() { // Multi-product case. transaction { - input { 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 = megaIssuedPounds)) } + 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 { 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 = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.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() } @@ -717,26 +718,26 @@ class ObligationTests { fun multiIssuer() { transaction { // Gather 2000 dollars from two different issuers. - input { inState } - input { inState `issued by` MINI_CORP } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP } // Can't merge them together. 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() } this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // This works. - output { 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)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this.verifies() } @@ -747,10 +748,10 @@ class ObligationTests { // Check we can do an atomic currency trade tx. transaction { val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY)) - input { inState `owned by` CHARLIE } - input { pounds } - output { inState `owned by` AnonymousParty(BOB_PUBKEY) } - output { pounds `owned by` CHARLIE } + input(OBLIGATION_PROGRAM_ID) { inState `owned by` CHARLIE } + input(OBLIGATION_PROGRAM_ID) { pounds } + output(OBLIGATION_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } + output(OBLIGATION_PROGRAM_ID) { pounds `owned by` CHARLIE } command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() } this.verifies() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt index fdd79cfee5..0ae01652a3 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt @@ -41,11 +41,11 @@ interface DummyContractBackdoor { fun inspectState(state: ContractState): Int } -val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContract() - class AttachmentClassLoaderTests : TestDependencyInjectionBase() { companion object { 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 { val serviceHub = mock() @@ -56,7 +56,6 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { class AttachmentDummyContract : Contract { data class State(val magicNumber: Int = 0) : ContractState { - override val contract = ATTACHMENT_TEST_PROGRAM_ID override val participants: List get() = listOf() } @@ -71,7 +70,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { 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`() { 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 - assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField("legalContractReference").value) + assertEquals("helloworld", contract.declaredField("magicString").value) } 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 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 assertEquals(cl, contract.javaClass.classLoader) - assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField("legalContractReference").value) + assertEquals("helloworld", contract.declaredField("magicString").value) } @@ -204,7 +203,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { fun createContract2Cash(): Contract { 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 } @@ -250,7 +249,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { 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) 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. 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) assertEquals(cl2, state3.contract.javaClass.classLoader) @@ -295,7 +294,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test 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 bytes = wireTransaction.serialize() val copiedWireTransaction = bytes.deserialize() @@ -307,13 +306,13 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test serialization of WireTransaction with dynamically loaded contract`() { 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 tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val storage = MockAttachmentStorage() val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass) - .withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child)) - .withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child)) + .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$State", true, child)) + .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$Commands\$Create", true, child)) .withAttachmentStorage(storage) // todo - think about better way to push attachmentStorage down to serializer @@ -325,14 +324,17 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { } val copiedWireTransaction = bytes.deserialize(context = context) 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)) } @Test fun `test deserialize of WireTransaction where contract cannot be found`() = kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { 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 tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val storage = MockAttachmentStorage() @@ -363,7 +365,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test loading a class from attachment during deserialization`() { 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 storage = MockAttachmentStorage() val attachmentRef = importJar(storage) @@ -380,7 +382,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test loading a class with attachment missing during deserialization`() { 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 storage = MockAttachmentStorage() val attachmentRef = SecureHash.randomSHA256() @@ -396,4 +398,4 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { } assertEquals(attachmentRef, e.ids.single()) } -} \ No newline at end of file +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt index c3f0ab3aaf..05bc015fad 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt @@ -163,7 +163,7 @@ class CordaClassResolverTests { val storage = MockAttachmentStorage() val attachmentHash = importJar(storage) 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) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 2584b160be..5121b3124d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -531,16 +531,14 @@ class SerializationOutputTests { } } + val FOO_PROGRAM_ID = "net.corda.nodeapi.internal.serialization.amqp.SerializationOutputTests.FooContract" class FooState : ContractState { - override val contract: Contract - get() = FooContract - override val participants: List - get() = emptyList() + override val participants: List = emptyList() } @Test 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()) AbstractAMQPSerializationScheme.registerCustomSerializers(factory) diff --git a/node-api/src/test/resources/net/corda/nodeapi/isolated.jar b/node-api/src/test/resources/net/corda/nodeapi/isolated.jar index 567a93ed9c64ed8e8076e690ea8d283adc49549a..ce003bd76d5502c4c8f2e6dabd000cb10ecb532a 100644 GIT binary patch literal 7977 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 WZeORFaBz37--p*uaGc=Vum1tIFk5N> diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index eaa4fcb2a2..211bc84fb2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -23,6 +23,7 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand 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 f = node.run { 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: services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture @@ -96,7 +97,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(clusterSize) node.run { val issueTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) + addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID) } database.transaction { services.recordTransactions(issueTx) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index f975a1225a..76ba4ae494 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getX500Name import net.corda.node.internal.AbstractNode 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.dummyCommand import net.corda.testing.node.NodeBasedTest @@ -45,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() { val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity) - addOutputState(dummyState) + addOutputState(dummyState, DUMMY_PROGRAM_ID) addCommand(dummyCommand(bankA.services.legalIdentityKey)) this } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index cf0e136b88..c1d1a0a586 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -10,6 +10,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY import net.corda.testing.aliceBobAndNotary +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand @@ -26,7 +27,7 @@ class LargeTransactionsTest { @Suspendable override fun call() { val tx = TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) .addAttachment(hash1) .addAttachment(hash2) diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index b6389806de..54d11abf56 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -1,13 +1,7 @@ package net.corda.test.node import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Command -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.contracts.* import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC @@ -71,7 +65,6 @@ fun isQuasarAgentSpecified(): Boolean { data class Message(val value: String) data class MessageState(val message: Message, val by: Party, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState { - override val contract = MessageContract() override val participants: List = listOf(by) override fun generateMappedObject(schema: MappedSchema): PersistentState { @@ -104,6 +97,8 @@ object MessageSchemaV1 : MappedSchema( ) : PersistentState() } +val MESSAGE_CONTRACT_PROGRAM_ID = "net.corda.test.node.MessageContract" + open class MessageContract : Contract { override fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand() @@ -146,7 +141,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic, List> { 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) } command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() } diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 696e27ee96..75ec51150a 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -15,6 +15,7 @@ import net.corda.node.internal.AbstractNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.getTestPartyAndCertificate @@ -142,9 +143,9 @@ class NotaryChangeTests { val tx = TransactionBuilder(null).apply { addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey)) - addOutputState(stateA, notary, encumbrance = 2) // Encumbered by stateB - addOutputState(stateC, notary) - addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC + addOutputState(stateA, DUMMY_PROGRAM_ID, notary, encumbrance = 2) // Encumbered by stateB + addOutputState(stateC, DUMMY_PROGRAM_ID, notary) + addOutputState(stateB, DUMMY_PROGRAM_ID, notary, encumbrance = 1) // Encumbered by stateC } val stx = node.services.signInitialTransaction(tx) node.services.recordTransactions(stx) @@ -170,7 +171,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> { fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef { 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 signedByA = nodeA.services.signInitialTransaction(tx) val signedByAB = nodeB.services.addSignature(signedByA) diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index e89aa57f8c..afe591c168 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -23,6 +23,7 @@ import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -131,9 +132,6 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { return ScheduledActivity(flowLogicRef, instant) } - - override val contract: Contract - get() = throw UnsupportedOperationException() } class TestFlowLogic(val increment: Int = 1) : FlowLogic() { @@ -282,7 +280,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { val freshKey = services.keyManagementService.freshKey() val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.legalIdentity) val builder = TransactionBuilder(null).apply { - addOutputState(state, DUMMY_NOTARY) + addOutputState(state, DUMMY_PROGRAM_ID, DUMMY_NOTARY) addCommand(Command(), freshKey) } val usefulTX = services.signInitialTransaction(builder, freshKey) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index a567ce1464..8bbe3f5643 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -21,6 +21,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork @@ -46,8 +47,7 @@ class ScheduledFlowTests { val source: Party, val destination: Party, val processed: Boolean = false, - override val linearId: UniqueIdentifier = UniqueIdentifier(), - override val contract: Contract = DummyContract()) : SchedulableState, LinearState { + override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { if (!processed) { val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) @@ -68,7 +68,7 @@ class ScheduledFlowTests { val notary = serviceHub.networkMapCache.getAnyNotary() val builder = TransactionBuilder(notary) - .addOutputState(scheduledState) + .addOutputState(scheduledState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity))) @@ -90,7 +90,7 @@ class ScheduledFlowTests { val newStateOutput = scheduledState.copy(processed = true) val builder = TransactionBuilder(notary) .addInputState(state) - .addOutputState(newStateOutput) + .addOutputState(newStateOutput, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index bbade152b5..336b7a24e7 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -12,6 +12,7 @@ import net.corda.node.services.api.SchemaService import net.corda.node.utilities.DatabaseTransactionManager import net.corda.node.utilities.configureDatabase 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.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService @@ -72,9 +73,6 @@ class HibernateObserverTests { throw UnsupportedOperationException() } - override val contract: Contract - get() = throw UnsupportedOperationException() - override val participants: List get() = throw UnsupportedOperationException() } @@ -101,7 +99,7 @@ class HibernateObserverTests { @Suppress("UNUSED_VARIABLE") val observer = HibernateObserver(rawUpdatesPublisher, database.hibernateConfig) 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() parentRowCountResult.next() val parentRows = parentRowCountResult.getInt(1) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 04aa485186..dda7feed32 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -31,6 +31,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer @@ -599,7 +600,7 @@ class FlowFrameworkTests { @Test fun `wait for transaction`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.services.legalIdentityKey)) val stx = node1.services.signInitialTransaction(ptx) @@ -614,7 +615,7 @@ class FlowFrameworkTests { @Test fun `committer throws exception before calling the finality flow`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand()) val stx = node1.services.signInitialTransaction(ptx) @@ -631,7 +632,7 @@ class FlowFrameworkTests { @Test fun `verify vault query service is tokenizable by force checkpointing within a flow`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.services.legalIdentityKey)) val stx = node1.services.signInitialTransaction(ptx) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 250b4fab5f..2197a3371a 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -231,8 +231,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Issue a linear state val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { - addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) - 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)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addCommand(dummyCommand(notaryServices.legalIdentityKey)) } val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder) @@ -252,7 +252,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { val dummyIssue = database.transaction { // Issue a linear state 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 dummyIssue = services.addSignature(dummyIssuePtx) @@ -266,7 +267,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Move the same state 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(0)) .addCommand(dummyCommand(notaryServices.legalIdentityKey)) @@ -340,8 +341,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Create a txn consuming different contract types val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { - addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity))) - addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity))) + addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)), DUMMY_DEAL_PROGRAM_ID) addInputState(linearStates.first()) addInputState(deals.first()) addCommand(dummyCommand(notaryServices.legalIdentityKey)) diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 1b24d48640..b8eec13bb7 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -116,7 +116,7 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu override fun call(): SignedTransaction { // Create a trivial transaction with an output that describes the attachment, and the attachment itself val ptx = TransactionBuilder(notary) - .addOutputState(AttachmentContract.State(hash)) + .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID) .addCommand(AttachmentContract.Command, serviceHub.legalIdentityKey) .addAttachment(hash) @@ -178,6 +178,8 @@ private fun printHelp(parser: OptionParser) { parser.printHelpOn(System.out) } +val ATTACHMENT_PROGRAM_ID = "net.corda.attachmentdemo.AttachmentContract" + class AttachmentContract : Contract { override fun verify(tx: LedgerTransaction) { val state = tx.outputsOfType().single() @@ -188,7 +190,6 @@ class AttachmentContract : Contract { object Command : TypeOnlyCommandData() data class State(val hash: SecureHash.SHA256) : ContractState { - override val contract: Contract = AttachmentContract() override val participants: List = emptyList() } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index 8a3d074e24..fa73c2571e 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -20,7 +20,7 @@ import java.math.RoundingMode import java.time.LocalDate 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 @CordaSerializable @@ -605,9 +605,6 @@ class InterestRateSwap : Contract { val common: Common, override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) ) : FixableDealState, SchedulableState { - - override val contract = IRS_PROGRAM_ID - override val oracleType: ServiceType 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 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? { @@ -721,7 +718,7 @@ class InterestRateSwap : Contract { // Put all the above into a new State object. 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 { @@ -736,6 +733,7 @@ class InterestRateSwap : Contract { val fixedRate = FixedRate(RatioUnit(fixing.value)) tx.addOutputState( irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.of.forDay, fixedRate)), + irs.state.contract, irs.state.notary ) tx.addCommand(Commands.Refix(fixing), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey)) diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 714840f777..f7ef2df219 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -3,7 +3,6 @@ package net.corda.irs.api import net.corda.core.contracts.Command import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState -import net.corda.core.contracts.`with notary` import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.generateKeyPair 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.contracts.Fix import net.corda.finance.contracts.FixOf -import net.corda.finance.contracts.asset.CASH -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.contracts.asset.* import net.corda.irs.flows.RatesFixFlow import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase @@ -246,7 +242,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { } 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()) } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index 338f723668..7fa0595e06 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -15,6 +15,7 @@ import java.math.BigDecimal import java.time.LocalDate import java.util.* import kotlin.test.assertEquals +import net.corda.irs.contract.IRS_PROGRAM_ID fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { return when (irsSelect) { @@ -369,7 +370,7 @@ class IRSTests : TestDependencyInjectionBase() { return ledger(initialiseSerialization = false) { transaction("Agreement") { - output("irs post agreement") { singleIRS() } + output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this.verifies() @@ -378,7 +379,7 @@ class IRSTests : TestDependencyInjectionBase() { transaction("Fix") { input("irs post agreement") val postAgreement = "irs post agreement".output() - output("irs post first fixing") { + output(IRS_PROGRAM_ID, "irs post first fixing") { postAgreement.copy( postAgreement.fixedLeg, postAgreement.floatingLeg, @@ -399,8 +400,8 @@ class IRSTests : TestDependencyInjectionBase() { fun `ensure failure occurs when there are inbound states for an agreement command`() { val irs = singleIRS() transaction(initialiseSerialization = false) { - input { irs } - output("irs post agreement") { irs } + input(IRS_PROGRAM_ID, irs) + output(IRS_PROGRAM_ID, "irs post agreement", irs) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are no in states for an agreement" @@ -412,9 +413,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val emptySchedule = mutableMapOf() transaction(initialiseSerialization = false) { - output { - irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule)) - } + output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are events in the fix schedule" @@ -426,9 +425,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val emptySchedule = mutableMapOf() transaction(initialiseSerialization = false) { - output { - irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule)) - } + output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are events in the float schedule" @@ -439,18 +436,14 @@ class IRSTests : TestDependencyInjectionBase() { fun `ensure notionals are non zero`() { val irs = singleIRS() transaction(initialiseSerialization = false) { - output { - irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0))) - } + output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" } transaction(initialiseSerialization = false) { - output { - irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0))) - } + output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" @@ -462,9 +455,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1")))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The fixed leg rate must be positive" @@ -479,9 +470,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY")))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The currency of the notionals must be the same" @@ -493,9 +482,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All leg notionals must be the same" @@ -507,9 +494,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1))) transaction(initialiseSerialization = false) { - output { - modifiedIRS1 - } + output(IRS_PROGRAM_ID, modifiedIRS1) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) 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))) transaction(initialiseSerialization = false) { - output { - modifiedIRS2 - } + output(IRS_PROGRAM_ID, modifiedIRS2) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) 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))) transaction(initialiseSerialization = false) { - output { - modifiedIRS3 - } + output(IRS_PROGRAM_ID, modifiedIRS3) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) 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))) transaction(initialiseSerialization = false) { - output { - modifiedIRS4 - } + output(IRS_PROGRAM_ID, modifiedIRS4) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The effective dates are aligned" @@ -559,7 +538,7 @@ class IRSTests : TestDependencyInjectionBase() { val bd = BigDecimal("0.0063518") transaction(initialiseSerialization = false) { - output("irs post agreement") { singleIRS() } + output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this.verifies() @@ -572,10 +551,7 @@ class IRSTests : TestDependencyInjectionBase() { oldIRS.common) transaction(initialiseSerialization = false) { - input { - oldIRS - - } + input(IRS_PROGRAM_ID, oldIRS) // Templated tweak for reference. A corrent fixing applied should be ok tweak { @@ -583,7 +559,7 @@ class IRSTests : TestDependencyInjectionBase() { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timeWindow(TEST_TX_TIME) - output { newIRS } + output(IRS_PROGRAM_ID, newIRS) this.verifies() } @@ -591,7 +567,7 @@ class IRSTests : TestDependencyInjectionBase() { tweak { command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } 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" } @@ -604,7 +580,7 @@ class IRSTests : TestDependencyInjectionBase() { val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) - output { + output(IRS_PROGRAM_ID) { newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, @@ -624,7 +600,7 @@ class IRSTests : TestDependencyInjectionBase() { 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"))) - output { + output(IRS_PROGRAM_ID) { newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, @@ -653,7 +629,7 @@ class IRSTests : TestDependencyInjectionBase() { return ledger(initialiseSerialization = false) { transaction("Agreement") { - output("irs post agreement1") { + output(IRS_PROGRAM_ID, "irs post agreement1") { irs.copy( irs.fixedLeg, irs.floatingLeg, @@ -667,7 +643,7 @@ class IRSTests : TestDependencyInjectionBase() { } transaction("Agreement") { - output("irs post agreement2") { + output(IRS_PROGRAM_ID, "irs post agreement2") { irs.copy( linearId = UniqueIdentifier("t2"), fixedLeg = irs.fixedLeg, @@ -685,7 +661,7 @@ class IRSTests : TestDependencyInjectionBase() { input("irs post agreement1") input("irs post agreement2") val postAgreement1 = "irs post agreement1".output() - output("irs post first fixing1") { + output(IRS_PROGRAM_ID, "irs post first fixing1") { postAgreement1.copy( postAgreement1.fixedLeg, postAgreement1.floatingLeg, @@ -694,7 +670,7 @@ class IRSTests : TestDependencyInjectionBase() { ) } val postAgreement2 = "irs post agreement2".output() - output("irs post first fixing2") { + output(IRS_PROGRAM_ID, "irs post first fixing2") { postAgreement2.copy( postAgreement2.fixedLeg, postAgreement2.floatingLeg, diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index 82e62ba997..af3c44c451 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -14,22 +14,21 @@ import net.corda.core.transactions.TransactionBuilder @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { + val DO_NOTHING_PROGRAM_ID = "net.corda.notarydemo.flows.DummyIssueAndMove.DoNothingContract" object DoNothingContract : Contract { override fun verify(tx: LedgerTransaction) {} } data class DummyCommand(val dummy: Int = 0): CommandData - data class State(override val participants: List, private val discriminator: Int) : ContractState { - override val contract = DoNothingContract - } + data class State(override val participants: List, private val discriminator: Int) : ContractState @Suspendable override fun call(): SignedTransaction { // Self issue an asset val state = State(listOf(serviceHub.myInfo.legalIdentity), discriminator) val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { - addOutputState(state) + addOutputState(state, DO_NOTHING_PROGRAM_ID) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) }) 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 return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) - addOutputState(state.copy(participants = listOf(counterpartyNode))) + addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) }) } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt index db1eff6755..e42c2653b8 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt @@ -1,6 +1,7 @@ package net.corda.vega.contracts import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.UniqueIdentifier import net.corda.core.crypto.keys import net.corda.core.identity.AbstractParty @@ -9,6 +10,8 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState 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. * @@ -17,13 +20,12 @@ import java.security.PublicKey data class IRSState(val swap: SwapData, val buyer: AbstractParty, val seller: AbstractParty, - override val contract: OGTrade, override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState { val ref: String get() = linearId.externalId!! // Same as the constructor for UniqueIdentified override val participants: List get() = listOf(buyer, seller) override fun generateAgreement(notary: Party): TransactionBuilder { - val state = IRSState(swap, buyer, seller, OGTrade()) - return TransactionBuilder(notary).withItems(state, Command(OGTrade.Commands.Agree(), participants.map { it.owningKey })) + val state = IRSState(swap, buyer, seller) + return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(OGTrade.Commands.Agree(), participants.map { it.owningKey })) } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index 02b1367d14..d98a414791 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -14,12 +14,13 @@ import java.time.LocalDate import java.time.ZoneOffset 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 * 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, - override val contract: PortfolioSwap, private val _parties: Pair, val valuationDate: LocalDate, val valuation: PortfolioValuation? = null, @@ -38,7 +39,7 @@ data class PortfolioState(val portfolio: List, } 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 { @@ -48,7 +49,7 @@ data class PortfolioState(val portfolio: List, val tx = TransactionBuilder(notary) 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 }) return tx } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index d167e347ae..1c317ea4f2 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -32,7 +32,7 @@ object IRSTradeFlow { } else { 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") val otherPartyAgreeFlag = sendAndReceive(otherParty, OfferMessage(notary, offer)).unwrap { it } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index e8565f65ec..7edd90048f 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -84,7 +84,7 @@ object SimmFlow { private fun agreePortfolio(portfolio: Portfolio) { logger.info("Agreeing portfolio") 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)) logger.info("Awaiting two party deal acceptor") diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt index b39ad233a5..afcb0e37ac 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt @@ -6,6 +6,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices @@ -35,7 +36,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() { val notaryServices = MockServices(DUMMY_NOTARY_KEY) val originBuilder = TransactionBuilder(DUMMY_NOTARY) - .addOutputState(DummyState(random31BitValue())) + .addOutputState(DummyState(random31BitValue()), DUMMY_PROGRAM_ID) .addCommand(command, MEGA_CORP_PUBKEY) val originPtx = megaCorpServices.signInitialTransaction(originBuilder) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt index c641c69b67..480f13ec34 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt @@ -9,6 +9,7 @@ import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import java.io.InputStream import java.security.KeyPair @@ -119,8 +120,8 @@ data class TestTransactionDSLInterpreter private constructor( transactionBuilder.addInputState(StateAndRef(state, stateRef)) } - override fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) { - transactionBuilder.addOutputState(contractState, notary, encumbrance) + override fun _output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) { + transactionBuilder.addOutputState(contractState, contractClassName, notary, encumbrance) if (label != null) { if (label in labelToIndexMap) { throw DuplicateOutputLabel(label) @@ -276,7 +277,7 @@ data class TestLedgerDSLInterpreter private constructor( private fun fillTransaction(transactionBuilder: TransactionBuilder) { if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand()) if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) { - transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE)) + transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE), DUMMY_PROGRAM_ID) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt index 544d050f54..8b7d382c3c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt @@ -34,8 +34,9 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { * @param notary The associated notary. * @param encumbrance The position of the encumbrance state. * @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. @@ -75,30 +76,30 @@ class TransactionDSL(val interpreter: T) : Tr * input to the current transaction. * @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)) { - output { state } + output(contractClassName) { state } } input(transaction.outRef(0).ref) } - fun input(stateClosure: () -> ContractState) = input(stateClosure()) + fun input(contractClassName: ContractClassName, stateClosure: () -> ContractState) = input(contractClassName, stateClosure()) /** * @see TransactionDSLInterpreter._output */ @JvmOverloads - fun output(label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) = - _output(label, notary, encumbrance, contractStateClosure()) + fun output(contractClassName: ContractClassName, label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) = + _output(contractClassName, label, notary, encumbrance, contractStateClosure()) /** * @see TransactionDSLInterpreter._output */ - fun output(label: String, contractState: ContractState) = - _output(label, DUMMY_NOTARY, null, contractState) + fun output(contractClassName: ContractClassName, label: String, contractState: ContractState) = + _output(contractClassName, label, DUMMY_NOTARY, null, contractState) - fun output(contractState: ContractState) = - _output(null, DUMMY_NOTARY, null, contractState) + fun output(contractClassName: ContractClassName, contractState: ContractState) = + _output(contractClassName,null, DUMMY_NOTARY, null, contractState) /** * @see TransactionDSLInterpreter._command diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 14f80dee98..9657ca997d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -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 -val DUMMY_PROGRAM_ID = DummyContract() +val DUMMY_PROGRAM_ID = "net.corda.testing.contracts.DummyContract" data class DummyContract(val blank: Any? = null) : Contract { 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 { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = listOf(owner) @@ -30,7 +29,6 @@ data class DummyContract(val blank: Any? = null) : Contract { */ data class MultiOwnerState(override val magicNumber: Int = 0, val owners: List) : ContractState, State { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = owners } @@ -49,10 +47,10 @@ data class DummyContract(val blank: Any? = null) : Contract { val owners = listOf(owner) + otherOwners return if (owners.size == 1) { 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 { 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( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), - /* OUTPUT */ state + /* OUTPUT */ StateAndContract(state, DUMMY_PROGRAM_ID) ) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index f8103acecc..1452e72a03 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -8,17 +8,16 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction // The dummy contract doesn't do anything useful. It exists for testing purposes. -val DUMMY_V2_PROGRAM_ID = DummyContractV2() +val DUMMY_V2_PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2" /** * Dummy contract state for testing of the upgrade process. */ // DOCSTART 1 class DummyContractV2 : UpgradedContract { - override val legacyContract = DummyContract::class.java + override val legacyContract = DummyContract::class.java.name data class State(val magicNumber: Int = 0, val owners: List) : ContractState { - override val contract = DUMMY_V2_PROGRAM_ID override val participants: List = owners } @@ -51,8 +50,8 @@ class DummyContractV2 : UpgradedContract, override val linearId: UniqueIdentifier) : DealState, QueryableState { - constructor(contract: Contract = DummyDealContract(), - participants: List = listOf(), - ref: String) : this(contract, participants, UniqueIdentifier(ref)) + constructor(participants: List = listOf(), + ref: String) : this(participants, UniqueIdentifier(ref)) override fun generateAgreement(notary: Party): TransactionBuilder { throw UnsupportedOperationException("not implemented") diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt index 408fefffd3..451e285114 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt @@ -16,6 +16,8 @@ import net.corda.testing.schemas.DummyLinearStateSchemaV2 import java.time.LocalDateTime import java.time.ZoneOffset.UTC +val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract" + class DummyLinearContract : Contract { override fun verify(tx: LedgerTransaction) { val inputs = tx.inputs.map { it.state.data }.filterIsInstance() @@ -31,16 +33,13 @@ class DummyLinearContract : Contract { data class State( override val linearId: UniqueIdentifier = UniqueIdentifier(), - override val contract: Contract = DummyLinearContract(), override val participants: List = listOf(), val linearString: String = "ABC", val linearNumber: Long = 123L, val linearTimestamp: java.time.Instant = LocalDateTime.now().toInstant(UTC), val linearBoolean: Boolean = true, val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState, QueryableState { - override fun supportedSchemas(): Iterable = listOf(DummyLinearStateSchemaV1, DummyLinearStateSchemaV2) - override fun generateMappedObject(schema: MappedSchema): PersistentState { return when (schema) { is DummyLinearStateSchemaV1 -> DummyLinearStateSchemaV1.PersistentDummyLinearState( diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt index c2e0696889..e1047cb2e3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt @@ -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]. */ data class DummyState(val magicNumber: Int = 0) : ContractState { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = emptyList() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt index aeec13aca4..78c19f58ab 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt @@ -41,7 +41,7 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List, val transactions: List = dealIds.map { // Issue a deal state 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()) } val stx = signInitialTransaction(dummyIssue) @@ -80,7 +80,7 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int, linearString = linearString, linearNumber = linearNumber, linearBoolean = linearBoolean, - linearTimestamp = linearTimestamp)) + linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addCommand(dummyCommand()) } @@ -221,7 +221,7 @@ fun ServiceHub.consumeAndProduce(stateAndRef: StateAndRef, // Create a txn consuming different contract types builder = TransactionBuilder(notary = notary).apply { 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)) } val producedTx = signInitialTransaction(builder, notary.owningKey) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 83d1e69f0c..4a94ec7555 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -134,8 +134,8 @@ class TransactionViewer : CordaView("Transactions") { val searchField = SearchField(transactions, "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) } }, - "Output" to { tx, s -> tx.outputs.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.contract.contains(s, true) } }, "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 } } }, "Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } } @@ -207,7 +207,7 @@ class TransactionViewer : CordaView("Transactions") { } private fun ObservableList>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } } - private fun ObservableList>.toText() = map { it.contract().javaClass.simpleName }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString() + private fun ObservableList>.toText() = map { it.contract() }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString() private class TransactionWidget : BorderPane() { private val partiallyResolvedTransactions by observableListReadOnly(TransactionDataModel::partiallyResolvedTransactions) @@ -299,7 +299,7 @@ class TransactionViewer : CordaView("Transactions") { } } - private fun StateAndRef.contract() = this.state.data.contract + private fun StateAndRef.contract() = this.state.contract } diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt index 21f576a7c3..86359414d8 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.AbstractAttachment import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.core.utilities.getX500Name import net.corda.testing.contracts.DummyContract import java.math.BigInteger @@ -62,7 +63,7 @@ data class GeneratedLedger( Generator.sequence( outputs.map { output -> 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>): Generator> { val outputsGen = outputsGenerator.map { outputs -> outputs.map { output -> - TransactionState(output, inputNotary, null) + TransactionState(output, DUMMY_PROGRAM_ID, inputNotary, null) } } val inputsGen = Generator.sampleBernoulli(inputsToChooseFrom) @@ -180,9 +181,7 @@ data class GeneratedLedger( data class GeneratedState( val nonce: Long, override val participants: List -) : ContractState { - override val contract = DummyContract() -} +) : ContractState class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) { override val id = bytes.sha256()