Merge branch 'master' into shams-merge-master-041217

# Conflicts:
#	node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt
#	samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaCordform.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/internal/NodeBasedTest.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/NotarySpec.kt
#	verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt
This commit is contained in:
Shams Asari
2017-12-04 13:39:32 +00:00
104 changed files with 1057 additions and 990 deletions

137
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,137 @@
# List of Contributors
We'd like to thank the following people for contributing ideas to Corda,
either during architecture review sessions of the R3 Architecture Working Group,
or in design reviews since Corda has been open-sourced. Some people have moved to
a different organisation since their contribution. Please forgive any omissions, and
create a pull request, or email <james@r3.com>, if you wish to see
changes to this list.
* Alberto Arri (R3)
* Andras Slemmer (R3)
* Andrius Dagys (R3)
* Andrzej Cichocki (R3)
* Anthony Coates (Deutsche Bank)
* Anton Semenov (Commerzbank)
* Antonio Cerrato (SEB)
* Anthony Woolley (Société Générale)
* Arnaud Stevens (Natixis)
* Arijit Das (Northern Trust)
* Arun Battu (BNY Mellon)
* Austin Moothart (R3)
* Barry Childe (HSBC)
* Barry Flower (Westpac)
* Benjamin Abineri (R3)
* Benoit Lafontaine (OCTO)
* Berit Bourgonje (ING)
* Bob Crozier (AIA)
* Bogdan Paunescu (R3)
* Cais Manai (R3)
* Carl Worrall (BCS)
* Chaitanya Jadhav (HSBC)
* Chris Akers (R3)
* Chris Burlinchon (R3)
* Chris Rankin (R3)
* Christian Kaufmann (Credit Suisse)
* Christian Sailer (R3)
* Christopher Saunders (Credit Suisse)
* Christopher Swanson (US Bank)
* Clark Thompson (R3)
* Clay Ratliff (Thoughtworks)
* Clemens Wan (R3)
* Clinton Alexander (R3)
* Daniel Roig (SEB)
* Dave Hudson (R3)
* David Lee (BCS)
* Farzad Pezeshkpour (RBS)
* Frederic Dalibard (Natixis)
* Garrett Macey (Wells Fargo)
* Gavin Thomas (R3)
* George Marcel Smetana (Bradesco)
* Giulio Katis (Westpac)
* Giuseppe Cardone (Intesa Sanpaolo)
* Guy Hochstetler (IBM)
* Ian Cusden (UBS)
* Ian Grigg (R3)
* Igor Nitto (R3)
* Igor Panov (CIBC)
* Ivan Schasny (R3)
* James Brown (R3)
* James Carlyle (R3)
* Jared Harwayne-Gidansky (BNY Mellon)
* Joel Dudley (R3)
* Johan Hörmark (SEB)
* Johann Palychata (BNP Paribas)
* Jonathan Sartin (R3)
* Jose Coll (R3)
* Jose Luu (Natixis)
* Josh Lindl (BCS)
* Justin Chapman (Northern Trust)
* Kai-Michael Schramm (Credit Suisse)
* Karel Hajek (Barclays Capital)
* Kasia Streich (R3)
* Kat Baker (R3)
* Khaild Ahmed (Northern Trust)
* Klaus Apolinario (Bradesco)
* Koen Vingerhoets (KBC)
* Kostas Chalkias (R3)
* Lars Stage Thomsen (Danske Bank)
* Lee Braine (Barclays)
* Lucas Salmen (Itau)
* Maksymillian Pawlak (R3)
* Marek Scocovsky (ABSA)
* Mark Lauer (Westpac)
* Mark Oldfield (R3)
* Mark Raynes (Thomson Reuters)
* Mark Simpson (RBS)
* Mark Tiggas (Wells Fargo)
* Massimo Morini (Banca IMI)
* Mat Rizzo (R3)
* Matt Britton (BCS)
* Matthew Nesbit (R3)
* Matthijs van den Bos (ING)
* Michal Kit (R3)
* Micheal Hinstridge (Thoughtworks)
* Michelle Sollecito (R3)
* Mike Hearn (R3)
* Mike Reichelt (US Bank)
* Mustafa Ozturk (Natixis)
* Nick Skinner (Northern Trust)
* Nigel King (R3)
* Nuam Athaweth (MUFG)
* Oscar Zibordi de Paiva (Bradesco)
* Patrick Kuo (R3)
* Pekka Kaipio (OP Financial)
* Piotr Piskorski (Nordea)
* Przemyslaw Bak (R3)
* Rex Maudsley (Société Générale)
* Richard Green (R3)
* Rick Parker (R3)
* Rhett Brewer (Goldman Sachs)
* Roberto Karpinski (Bradesco)
* Robin Green (CIBC)
* Rodrigo Bueno (Itau)
* Roger Willis (R3)
* Ross Burnett (Macquarie)
* Ross Nicoll (R3)
* Sajindra Jayasena (Deutsche Bank)
* Saket Sharma (BNY Mellon)
* Sam Chadwick (Thomson Reuters)
* Sasmit Sahu (Credit Suisse)
* Scott James (Credit Suisse)
* Shams Asari (R3)
* Simon Taylor (Barclays)
* Sofus Mortensen (Digital Asset Holdings)
* Szymon Sztuka (R3)
* Stephen Lane-Smith (BMO)
* Thomas O'Donnell (Macquarie)
* Thomas Schroeter (R3)
* Tom Menner (R3)
* Tudor Malene (R3)
* Tim Swanson (R3)
* Timothy Smith (Credit Suisse)
* Tommy Lillehagen (R3)
* Viktor Kolomeyko (R3)
* Wawrzek Niewodniczanski (R3)
* Wei Wu Zhang (Commonwealth Bank of Australia)
* Zabrina Smith (Northern Trust)

View File

@ -28,7 +28,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test

View File

@ -10,7 +10,7 @@ import net.corda.finance.flows.CashPaymentFlow;
import net.corda.finance.schemas.CashSchemaV1; import net.corda.finance.schemas.CashSchemaV1;
import net.corda.node.internal.Node; import net.corda.node.internal.Node;
import net.corda.node.internal.StartedNode; import net.corda.node.internal.StartedNode;
import net.corda.nodeapi.User; import net.corda.nodeapi.internal.config.User;
import net.corda.testing.CoreTestUtils; import net.corda.testing.CoreTestUtils;
import net.corda.testing.internal.NodeBasedTest; import net.corda.testing.internal.NodeBasedTest;
import org.junit.After; import org.junit.After;

View File

@ -20,7 +20,7 @@ import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.internal.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException

View File

@ -15,7 +15,7 @@ import net.corda.core.utilities.*
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ActiveMQClient

View File

@ -9,7 +9,7 @@ import net.corda.core.messaging.FlowHandle;
import net.corda.core.utilities.OpaqueBytes; import net.corda.core.utilities.OpaqueBytes;
import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.AbstractCashFlow;
import net.corda.finance.flows.CashIssueFlow; import net.corda.finance.flows.CashIssueFlow;
import net.corda.nodeapi.User; import net.corda.nodeapi.internal.config.User;
import net.corda.smoketesting.NodeConfig; import net.corda.smoketesting.NodeConfig;
import net.corda.smoketesting.NodeProcess; import net.corda.smoketesting.NodeProcess;
import org.junit.After; import org.junit.After;

View File

@ -21,7 +21,7 @@ import net.corda.finance.contracts.getCashBalance
import net.corda.finance.contracts.getCashBalances import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import org.apache.commons.io.output.NullOutputStream import org.apache.commons.io.output.NullOutputStream

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.map
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.services.messaging.RPCServerConfiguration import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.internal.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.internal.rpcTestUser import net.corda.testing.internal.rpcTestUser

View File

@ -4,7 +4,7 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.messaging.rpcContext import net.corda.node.services.messaging.rpcContext
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.internal.RPCDriverExposedDSLInterface import net.corda.testing.internal.RPCDriverExposedDSLInterface
import net.corda.testing.internal.rpcDriver import net.corda.testing.internal.rpcDriver
import org.junit.Test import org.junit.Test

View File

@ -7,7 +7,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import net.corda.testing.common.internal.ProjectStructure import net.corda.testing.common.internal.ProjectStructure

View File

@ -11,7 +11,7 @@ import net.corda.core.internal.list
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME

View File

@ -38,24 +38,20 @@ class PartialMerkleTreeTest {
testLedger = ledger { testLedger = ledger {
unverifiedTransaction { unverifiedTransaction {
attachments(Cash.PROGRAM_ID) attachments(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID, "MEGA_CORP cash") { output(Cash.PROGRAM_ID, "MEGA_CORP cash",
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP owner = MEGA_CORP))
) output(Cash.PROGRAM_ID, "dummy cash 1",
}
output(Cash.PROGRAM_ID, "dummy cash 1") {
Cash.State( Cash.State(
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MINI_CORP owner = MINI_CORP))
)
}
} }
transaction { transaction {
attachments(Cash.PROGRAM_ID) attachments(Cash.PROGRAM_ID)
input("MEGA_CORP cash") input("MEGA_CORP cash")
output(Cash.PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP)) output(Cash.PROGRAM_ID, "MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }

View File

@ -19,7 +19,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.node.internal.SecureCordaRPCOps import net.corda.node.internal.SecureCordaRPCOps
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2 import net.corda.testing.contracts.DummyContractV2

View File

@ -20,6 +20,42 @@ class ReceiveMultipleFlowTests {
mockNet.stopNodes() mockNet.stopNodes()
} }
@Test
fun showcase_flows_as_closures() {
val answer = 10.0
val message = "Hello Ivan"
val counterParty = nodes[1].info.singleIdentity()
val initiatingFlow = @InitiatingFlow object : FlowLogic<Any>() {
@Suspendable
override fun call(): Any {
val session = initiateFlow(counterParty)
return session.sendAndReceive<Any>(message).unwrap { it }
}
}
nodes[1].registerInitiatedFlow(initiatingFlow::class) { session ->
object : FlowLogic<Unit>() {
@Suspendable
override fun call() {
// this is a closure, meaning you can access variables outside its scope e.g., `answer`.
val receivedMessage = session.receive<String>().unwrap { it }
logger.info("Got message from counterParty: $receivedMessage.")
assertThat(receivedMessage).isEqualTo(message)
session.send(answer)
}
} as FlowLogic<Unit>
}
val flow = nodes[0].services.startFlow(initiatingFlow)
mockNet.runNetwork()
val receivedAnswer = flow.resultFuture.getOrThrow()
assertThat(receivedAnswer).isEqualTo(answer)
}
@Test @Test
fun `receive all messages in parallel using map style`() { fun `receive all messages in parallel using map style`() {
val doubleValue = 5.0 val doubleValue = 5.0

View File

@ -55,10 +55,10 @@ class TransactionEncumbranceTests {
ledger { ledger {
transaction { transaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
input(Cash.PROGRAM_ID) { state } input(Cash.PROGRAM_ID, state)
output(Cash.PROGRAM_ID, encumbrance = 1) { stateWithNewOwner } output(Cash.PROGRAM_ID, encumbrance = 1, contractState = stateWithNewOwner)
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
verifies() verifies()
} }
} }
@ -69,16 +69,16 @@ class TransactionEncumbranceTests {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state } output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state)
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
} }
// Un-encumber the output if the time of the transaction is later than the timelock. // Un-encumber the output if the time of the transaction is later than the timelock.
transaction { transaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output(Cash.PROGRAM_ID) { stateWithNewOwner } output(Cash.PROGRAM_ID, stateWithNewOwner)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
verifies() verifies()
} }
@ -90,16 +90,16 @@ class TransactionEncumbranceTests {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state } output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state)
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
} }
// The time of the transaction is earlier than the time specified in the encumbering timelock. // The time of the transaction is earlier than the time specified in the encumbering timelock.
transaction { transaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output(Cash.PROGRAM_ID) { state } output(Cash.PROGRAM_ID, state)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
timeWindow(FOUR_PM) timeWindow(FOUR_PM)
this `fails with` "the time specified in the time-lock has passed" this `fails with` "the time specified in the time-lock has passed"
} }
@ -111,14 +111,14 @@ class TransactionEncumbranceTests {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state } output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = state)
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
} }
transaction { transaction {
attachments(Cash.PROGRAM_ID) attachments(Cash.PROGRAM_ID)
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
output(Cash.PROGRAM_ID) { stateWithNewOwner } output(Cash.PROGRAM_ID, stateWithNewOwner)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"
} }
@ -130,9 +130,9 @@ class TransactionEncumbranceTests {
ledger { ledger {
transaction { transaction {
attachments(Cash.PROGRAM_ID) attachments(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { state } input(Cash.PROGRAM_ID, state)
output(Cash.PROGRAM_ID, encumbrance = 0) { stateWithNewOwner } output(Cash.PROGRAM_ID, encumbrance = 0, contractState = stateWithNewOwner)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
this `fails with` "Missing required encumbrance 0 in OUTPUT" this `fails with` "Missing required encumbrance 0 in OUTPUT"
} }
} }
@ -143,10 +143,10 @@ class TransactionEncumbranceTests {
ledger { ledger {
transaction { transaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
input(Cash.PROGRAM_ID) { state } input(Cash.PROGRAM_ID, state)
output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner } output(TEST_TIMELOCK_ID, encumbrance = 2, contractState = stateWithNewOwner)
output(TEST_TIMELOCK_ID) { timeLock } output(TEST_TIMELOCK_ID, timeLock)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
this `fails with` "Missing required encumbrance 2 in OUTPUT" this `fails with` "Missing required encumbrance 2 in OUTPUT"
} }
} }
@ -157,16 +157,16 @@ class TransactionEncumbranceTests {
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state } output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1, contractState = state)
output(Cash.PROGRAM_ID, "some other state") { state } output(Cash.PROGRAM_ID, "some other state", state)
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
} }
transaction { transaction {
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID) attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
input("state encumbered by some other state") input("state encumbered by some other state")
input("5pm time-lock") input("5pm time-lock")
output(Cash.PROGRAM_ID) { stateWithNewOwner } output(Cash.PROGRAM_ID, stateWithNewOwner)
command(MEGA_CORP.owningKey) { Cash.Commands.Move() } command(MEGA_CORP.owningKey, Cash.Commands.Move())
timeWindow(FIVE_PM) timeWindow(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"
} }

View File

@ -122,11 +122,11 @@ handling, and ensures the Corda service is run at boot.
9. Provision the required certificates to your node. Contact the network permissioning service or see 9. Provision the required certificates to your node. Contact the network permissioning service or see
:doc:`permissioning` :doc:`permissioning`
10. You can now start a node and its webserver by running the following ``systemctl`` commands: 10. You can now start a node and its webserver and set the services to start on boot by running the following ``systemctl`` commands:
* ``sudo systemctl daemon-reload`` * ``sudo systemctl daemon-reload``
* ``sudo systemctl corda start`` * ``sudo systemctl enable --now corda``
* ``sudo systemctl corda-webserver start`` * ``sudo systemctl enable --now corda-webserver``
You can run multiple nodes by creating multiple directories and Corda services, modifying the ``node.conf`` and You can run multiple nodes by creating multiple directories and Corda services, modifying the ``node.conf`` and
``service`` files so they are unique. ``service`` files so they are unique.
@ -204,10 +204,11 @@ at boot, and means the Corda service stays running with no users connected to th
nssm install cordanode1 C:\ProgramData\Oracle\Java\javapath\java.exe nssm install cordanode1 C:\ProgramData\Oracle\Java\javapath\java.exe
nssm set cordanode1 AppDirectory C:\Corda nssm set cordanode1 AppDirectory C:\Corda
nssm set cordanode1 AppParameters "-jar corda.jar -Xmx2048m --config-file=C:\corda\node.conf" nssm set cordanode1 AppParameters "-Xmx2048m -jar corda.jar --config-file=C:\corda\node.conf"
nssm set cordanode1 AppStdout C:\Corda\service.log nssm set cordanode1 AppStdout C:\Corda\service.log
nssm set cordanode1 AppStderr C:\Corda\service.log nssm set cordanode1 AppStderr C:\Corda\service.log
nssm set cordanode1 Description Corda Node - Bank of Breakfast Tea nssm set cordanode1 Description Corda Node - Bank of Breakfast Tea
nssm set cordanode1 Start SERVICE_AUTO_START
sc start cordanode1 sc start cordanode1
9. Modify the batch file: 9. Modify the batch file:

View File

@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test

View File

@ -17,7 +17,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.graphstream.graph.Edge import org.graphstream.graph.Edge

View File

@ -24,6 +24,7 @@ object CustomVaultQuery {
private companion object { private companion object {
private val log = contextLogger() private val log = contextLogger()
} }
fun rebalanceCurrencyReserves(): List<Amount<Currency>> { fun rebalanceCurrencyReserves(): List<Amount<Currency>> {
val nativeQuery = """ val nativeQuery = """
select select
@ -44,16 +45,18 @@ object CustomVaultQuery {
""" """
log.info("SQL to execute: $nativeQuery") log.info("SQL to execute: $nativeQuery")
val session = services.jdbcSession() val session = services.jdbcSession()
val prepStatement = session.prepareStatement(nativeQuery) return session.prepareStatement(nativeQuery).use { prepStatement ->
val rs = prepStatement.executeQuery() prepStatement.executeQuery().use { rs ->
val topUpLimits: MutableList<Amount<Currency>> = mutableListOf() val topUpLimits: MutableList<Amount<Currency>> = mutableListOf()
while (rs.next()) { while (rs.next()) {
val currencyStr = rs.getString(1) val currencyStr = rs.getString(1)
val amount = rs.getLong(2) val amount = rs.getLong(2)
log.info("$currencyStr : $amount") log.info("$currencyStr : $amount")
topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr))) topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr)))
}
topUpLimits
}
} }
return topUpLimits
} }
} }
} }
@ -69,6 +72,7 @@ object TopupIssuerFlow {
data class TopupRequest(val issueToParty: Party, data class TopupRequest(val issueToParty: Party,
val issuerPartyRef: OpaqueBytes, val issuerPartyRef: OpaqueBytes,
val notaryParty: Party) val notaryParty: Party)
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
class TopupIssuanceRequester(val issueToParty: Party, class TopupIssuanceRequester(val issueToParty: Party,

View File

@ -33,7 +33,7 @@ class CommercialPaperTest {
ledger { ledger {
transaction { transaction {
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
input(CP_PROGRAM_ID) { inState } input(CP_PROGRAM_ID, inState)
verifies() verifies()
} }
} }
@ -46,8 +46,8 @@ class CommercialPaperTest {
val inState = getPaper() val inState = getPaper()
ledger { ledger {
transaction { transaction {
input(CP_PROGRAM_ID) { inState } input(CP_PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
verifies() verifies()
} }
@ -61,8 +61,8 @@ class CommercialPaperTest {
val inState = getPaper() val inState = getPaper()
ledger { ledger {
transaction { transaction {
input(CP_PROGRAM_ID) { inState } input(CP_PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
`fails with`("the state is propagated") `fails with`("the state is propagated")
} }
@ -76,11 +76,11 @@ class CommercialPaperTest {
val inState = getPaper() val inState = getPaper()
ledger { ledger {
transaction { transaction {
input(CP_PROGRAM_ID) { inState } input(CP_PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
`fails with`("the state is propagated") `fails with`("the state is propagated")
output(CP_PROGRAM_ID, "alice's paper") { inState.withOwner(ALICE) } output(CP_PROGRAM_ID, "alice's paper", inState.withOwner(ALICE))
verifies() verifies()
} }
} }
@ -92,15 +92,15 @@ class CommercialPaperTest {
fun `simple issuance with tweak`() { fun `simple issuance with tweak`() {
ledger { ledger {
transaction { transaction {
output(CP_PROGRAM_ID, "paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp. output(CP_PROGRAM_ID, "paper", getPaper()) // Some CP is issued onto the ledger by MegaCorp.
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
tweak { tweak {
// The wrong pubkey. // The wrong pubkey.
command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
`fails with`("output states are issued by a command signer") `fails with`("output states are issued by a command signer")
} }
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
verifies() verifies()
} }
@ -112,15 +112,15 @@ class CommercialPaperTest {
@Test @Test
fun `simple issuance with tweak and top level transaction`() { fun `simple issuance with tweak and top level transaction`() {
transaction { transaction {
output(CP_PROGRAM_ID, "paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp. output(CP_PROGRAM_ID, "paper", getPaper()) // Some CP is issued onto the ledger by MegaCorp.
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
tweak { tweak {
// The wrong pubkey. // The wrong pubkey.
command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
`fails with`("output states are issued by a command signer") `fails with`("output states are issued by a command signer")
} }
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
verifies() verifies()
} }
@ -140,8 +140,8 @@ class CommercialPaperTest {
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") { transaction("Issuance") {
output(CP_PROGRAM_ID, "paper") { getPaper() } output(CP_PROGRAM_ID, "paper", getPaper())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
verifies() verifies()
@ -151,10 +151,10 @@ class CommercialPaperTest {
transaction("Trade") { transaction("Trade") {
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP)
output(CP_PROGRAM_ID, "alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) } output(CP_PROGRAM_ID, "alice's paper", "paper".output<ICommercialPaperState>().withOwner(ALICE))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
verifies() verifies()
} }
} }
@ -173,8 +173,8 @@ class CommercialPaperTest {
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") { transaction("Issuance") {
output(CP_PROGRAM_ID, "paper") { getPaper() } output(CP_PROGRAM_ID, "paper", getPaper())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
verifies() verifies()
@ -183,18 +183,18 @@ class CommercialPaperTest {
transaction("Trade") { transaction("Trade") {
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP)
output(CP_PROGRAM_ID, "alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) } output(CP_PROGRAM_ID, "alice's paper", "paper".output<ICommercialPaperState>().withOwner(ALICE))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
verifies() verifies()
} }
transaction { transaction {
input("paper") input("paper")
// We moved a paper to another pubkey. // We moved a paper to another pubkey.
output(CP_PROGRAM_ID, "bob's paper") { "paper".output<ICommercialPaperState>().withOwner(BOB) } output(CP_PROGRAM_ID, "bob's paper", "paper".output<ICommercialPaperState>().withOwner(BOB))
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
verifies() verifies()
} }
@ -215,8 +215,8 @@ class CommercialPaperTest {
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") { transaction("Issuance") {
output(CP_PROGRAM_ID, "paper") { getPaper() } output(CP_PROGRAM_ID, "paper", getPaper())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
attachments(CP_PROGRAM_ID) attachments(CP_PROGRAM_ID)
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
verifies() verifies()
@ -225,10 +225,10 @@ class CommercialPaperTest {
transaction("Trade") { transaction("Trade") {
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP)
output(CP_PROGRAM_ID, "alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) } output(CP_PROGRAM_ID, "alice's paper", "paper".output<ICommercialPaperState>().withOwner(ALICE))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
verifies() verifies()
} }
@ -236,8 +236,8 @@ class CommercialPaperTest {
transaction { transaction {
input("paper") input("paper")
// We moved a paper to another pubkey. // We moved a paper to another pubkey.
output(CP_PROGRAM_ID, "bob's paper") { "paper".output<ICommercialPaperState>().withOwner(BOB) } output(CP_PROGRAM_ID, "bob's paper", "paper".output<ICommercialPaperState>().withOwner(BOB))
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() } command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
verifies() verifies()
} }
fails() fails()

View File

@ -22,6 +22,12 @@ Start the nodes with ``runnodes`` by running the following command from the root
.. warn:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may .. warn:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may
fail to start. fail to start.
If you receive an ``OutOfMemoryError`` exception when interacting with the nodes, you need to increase the amount of
Java heap memory available to them, which you can do when running them individually. See
:ref:`starting-an-individual-corda-node`.
.. _starting-an-individual-corda-node:
Starting an individual Corda node Starting an individual Corda node
--------------------------------- ---------------------------------
Run the node by opening a terminal window in the node's folder and running: Run the node by opening a terminal window in the node's folder and running:
@ -30,9 +36,18 @@ Run the node by opening a terminal window in the node's folder and running:
java -jar corda.jar java -jar corda.jar
.. warning:: By default, the node will look for a configuration file called ``node.conf`` and a CorDapps folder called By default, the node will look for a configuration file called ``node.conf`` and a CorDapps folder called ``cordapps``
``cordapps`` in the current working directory. You can override the configuration file and workspace paths on the in the current working directory. You can override the configuration file and workspace paths on the command line (e.g.
command line (e.g. ``./corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``). ``./corda.jar --config-file=test.conf --base-directory=/opt/corda/nodes/test``).
You can increase the amount of Java heap memory available to the node using the ``-Xmx`` command line argument. For
example, the following would run the node with a heap size of 2048MB:
.. code-block:: shell
java -Xmx2048m -jar corda.jar
You should do this if you receive an ``OutOfMemoryError`` exception when interacting with the node.
Optionally run the node's webserver as well by opening a terminal window in the node's folder and running: Optionally run the node's webserver as well by opening a terminal window in the node's folder and running:

View File

@ -172,16 +172,14 @@ class Cap {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { stateInitial } output(UNIVERSAL_PROGRAM_ID, stateInitial)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -189,44 +187,38 @@ class Cap {
@Test @Test
fun `first fixing`() { fun `first fixing`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateInitial } input(UNIVERSAL_PROGRAM_ID, stateInitial)
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
// wrong source // wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong date // wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong tenor // wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))))
this `fails with` "output state does not reflect fix command" this `fails with` "output state does not reflect fix command"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))))
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) }
this.verifies() this.verifies()
} }
} }
@ -234,19 +226,16 @@ class Cap {
@Test @Test
fun `first execute`() { fun `first execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies() this.verifies()
} }
} }
@ -254,18 +243,15 @@ class Cap {
@Test @Test
fun `final execute`() { fun `final execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal)
output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal } output(UNIVERSAL_PROGRAM_ID, statePaymentFinal)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies() this.verifies()
} }
} }
@ -273,44 +259,38 @@ class Cap {
@Test @Test
fun `second fixing`() { fun `second fixing`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } input(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
// wrong source // wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong date // wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01").plusYears(1), Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01").plusYears(1), Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong tenor // wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("9M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("9M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.5.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.5.bd))))
this `fails with` "output state does not reflect fix command" this `fails with` "output state does not reflect fix command"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))))
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", BusinessCalendar.parseDateFromString("2017-03-01"), Tenor("3M")), 1.0.bd))) }
this.verifies() this.verifies()
} }
} }

View File

@ -55,16 +55,14 @@ class Caplet {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { stateStart } output(UNIVERSAL_PROGRAM_ID, stateStart)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -72,17 +70,15 @@ class Caplet {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateFixed } input(UNIVERSAL_PROGRAM_ID, stateFixed)
output(UNIVERSAL_PROGRAM_ID) { stateFinal } output(UNIVERSAL_PROGRAM_ID, stateFinal)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies() this.verifies()
} }
} }
@ -90,44 +86,38 @@ class Caplet {
@Test @Test
fun `fixing`() { fun `fixing`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateStart } input(UNIVERSAL_PROGRAM_ID, stateStart)
output(UNIVERSAL_PROGRAM_ID) { stateFixed } output(UNIVERSAL_PROGRAM_ID, stateFixed)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
// wrong source // wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong date // wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong tenor // wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))))
this `fails with` "output state does not reflect fix command" this `fails with` "output state does not reflect fix command"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))))
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) }
this.verifies() this.verifies()
} }
} }

View File

@ -52,20 +52,18 @@ class FXFwdTimeOption {
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { inState } output(UNIVERSAL_PROGRAM_ID, inState)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue())
command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -73,31 +71,28 @@ class FXFwdTimeOption {
@Test @Test
fun `maturity, bank exercise`() { fun `maturity, bank exercise`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_AFTER_MATURITY) timeWindow(TEST_TX_TIME_AFTER_MATURITY)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") } command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") } command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") }
this.verifies() this.verifies()
} }
} }
@ -105,31 +100,28 @@ class FXFwdTimeOption {
@Test @Test
fun `maturity, corp exercise`() { fun `maturity, corp exercise`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_BEFORE_MATURITY) timeWindow(TEST_TX_TIME_BEFORE_MATURITY)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(acmeCorp.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") } command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies() this.verifies()
} }
} }

View File

@ -44,20 +44,18 @@ class FXSwap {
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { inState } output(UNIVERSAL_PROGRAM_ID, inState)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue())
command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -65,18 +63,16 @@ class FXSwap {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
this.verifies() this.verifies()
} }
} }
@ -84,18 +80,16 @@ class FXSwap {
@Test @Test
fun `execute - reversed order`() { fun `execute - reversed order`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
this.verifies() this.verifies()
} }
} }
@ -103,12 +97,11 @@ class FXSwap {
@Test @Test
fun `execute - not authorized`() { fun `execute - not authorized`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(momAndPop.owningKey, UniversalContract.Commands.Action("execute"))
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
} }
@ -116,12 +109,11 @@ class FXSwap {
@Test @Test
fun `execute - before maturity`() { fun `execute - before maturity`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_TOO_EARLY) timeWindow(TEST_TX_TIME_TOO_EARLY)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
} }
@ -129,11 +121,10 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 1`() { fun `execute - outState mismatch 1`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "output state must match action result state" this `fails with` "output state must match action result state"
} }
} }
@ -141,12 +132,11 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 2`() { fun `execute - outState mismatch 2`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outStateBad2 } output(UNIVERSAL_PROGRAM_ID, outStateBad2)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "output states must match action result state" this `fails with` "output states must match action result state"
} }
} }
@ -154,12 +144,11 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 3`() { fun `execute - outState mismatch 3`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outStateBad1 } output(UNIVERSAL_PROGRAM_ID, outStateBad1)
output(UNIVERSAL_PROGRAM_ID) { outState2 } output(UNIVERSAL_PROGRAM_ID, outState2)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "output states must match action result state" this `fails with` "output states must match action result state"
} }
} }
@ -167,12 +156,11 @@ class FXSwap {
@Test @Test
fun `execute - outState mismatch 4`() { fun `execute - outState mismatch 4`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState1 } output(UNIVERSAL_PROGRAM_ID, outState1)
output(UNIVERSAL_PROGRAM_ID) { outStateBad3 } output(UNIVERSAL_PROGRAM_ID, outStateBad3)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "output states must match action result state" this `fails with` "output states must match action result state"
} }
} }

View File

@ -134,16 +134,14 @@ class IRS {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { stateInitial } output(UNIVERSAL_PROGRAM_ID, stateInitial)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -151,44 +149,38 @@ class IRS {
@Test @Test
fun `first fixing`() { fun `first fixing`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateInitial } input(UNIVERSAL_PROGRAM_ID, stateInitial)
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
tweak { tweak {
// wrong source // wrong source
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong date // wrong date
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
// wrong tenor // wrong tenor
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))))
this `fails with` "relevant fixing must be included" this `fails with` "relevant fixing must be included"
} }
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))) } command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))))
this `fails with` "output state does not reflect fix command" this `fails with` "output state does not reflect fix command"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))))
command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(net.corda.finance.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) }
this.verifies() this.verifies()
} }
} }
@ -196,19 +188,16 @@ class IRS {
@Test @Test
fun `first execute`() { fun `first execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("pay floating"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("pay floating") }
this.verifies() this.verifies()
} }
} }

View File

@ -145,16 +145,14 @@ class RollOutTests {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { stateStart } output(UNIVERSAL_PROGRAM_ID, stateStart)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -162,18 +160,16 @@ class RollOutTests {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { stateStart } input(UNIVERSAL_PROGRAM_ID, stateStart)
output(UNIVERSAL_PROGRAM_ID) { stateStep1a } output(UNIVERSAL_PROGRAM_ID, stateStep1a)
output(UNIVERSAL_PROGRAM_ID) { stateStep1b } output(UNIVERSAL_PROGRAM_ID, stateStep1b)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
/* tweak { /* tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails with` "action must be defined" this `fails with` "action must be defined"
}*/ }*/
command(highStreetBank.owningKey, UniversalContract.Commands.Action("transfer"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("transfer") }
this.verifies() this.verifies()
} }
} }

View File

@ -61,16 +61,14 @@ class Swaption {
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { stateInitial } output(UNIVERSAL_PROGRAM_ID, stateInitial)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }

View File

@ -51,15 +51,12 @@ class ZeroCouponBond {
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {
transaction { transaction {
output(UNIVERSAL_PROGRAM_ID) { inState } output(UNIVERSAL_PROGRAM_ID, inState)
tweak { tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -67,17 +64,15 @@ class ZeroCouponBond {
@Test @Test
fun `execute`() { fun `execute`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState } output(UNIVERSAL_PROGRAM_ID, outState)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
tweak { tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } command(highStreetBank.owningKey, UniversalContract.Commands.Action("some undefined name"))
this `fails with` "action must be defined" this `fails with` "action must be defined"
} }
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
this.verifies() this.verifies()
} }
} }
@ -85,11 +80,10 @@ class ZeroCouponBond {
@Test @Test
fun `execute - not authorized`() { fun `execute - not authorized`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outState } output(UNIVERSAL_PROGRAM_ID, outState)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(momAndPop.owningKey, UniversalContract.Commands.Action("execute"))
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "condition must be met" this `fails with` "condition must be met"
} }
} }
@ -97,11 +91,10 @@ class ZeroCouponBond {
@Test @Test
fun `execute - outState mismatch`() { fun `execute - outState mismatch`() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
output(UNIVERSAL_PROGRAM_ID) { outStateWrong } output(UNIVERSAL_PROGRAM_ID, outStateWrong)
timeWindow(TEST_TX_TIME_1) timeWindow(TEST_TX_TIME_1)
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "output state must match action result state" this `fails with` "output state must match action result state"
} }
} }
@ -109,29 +102,23 @@ class ZeroCouponBond {
@Test @Test
fun move() { fun move() {
transaction { transaction {
input(UNIVERSAL_PROGRAM_ID) { inState } input(UNIVERSAL_PROGRAM_ID, inState)
tweak { tweak {
output(UNIVERSAL_PROGRAM_ID) { outStateMove } output(UNIVERSAL_PROGRAM_ID, outStateMove)
command(acmeCorp.owningKey) { command(acmeCorp.owningKey,
UniversalContract.Commands.Move(acmeCorp, momAndPop) UniversalContract.Commands.Move(acmeCorp, momAndPop))
}
this `fails with` "the transaction is signed by all liable parties" this `fails with` "the transaction is signed by all liable parties"
} }
tweak { tweak {
output(UNIVERSAL_PROGRAM_ID) { inState } output(UNIVERSAL_PROGRAM_ID, inState)
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey),
UniversalContract.Commands.Move(acmeCorp, momAndPop) UniversalContract.Commands.Move(acmeCorp, momAndPop))
}
this `fails with` "output state does not reflect move command" this `fails with` "output state does not reflect move command"
} }
output(UNIVERSAL_PROGRAM_ID, outStateMove)
output(UNIVERSAL_PROGRAM_ID) { outStateMove } command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey),
UniversalContract.Commands.Move(acmeCorp, momAndPop))
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
UniversalContract.Commands.Move(acmeCorp, momAndPop)
}
this.verifies() this.verifies()
} }
} }

View File

@ -69,13 +69,14 @@ abstract class AbstractCashSelection {
* with this notary are included. * with this notary are included.
* @param onlyFromIssuerParties Optional issuer parties to match against. * @param onlyFromIssuerParties Optional issuer parties to match against.
* @param withIssuerRefs Optional issuer references to match against. * @param withIssuerRefs Optional issuer references to match against.
* @return JDBC ResultSet with the matching states that were found. If sufficient funds were found these will be locked, * @param withResultSet Function that contains the business logic. The JDBC ResultSet with the matching states that were found. If sufficient funds were found these will be locked,
* otherwise what is available is returned unlocked for informational purposes. * otherwise what is available is returned unlocked for informational purposes.
* @return The result of the withResultSet function
*/ */
abstract fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, abstract fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean
override abstract fun toString() : String override abstract fun toString(): String
/** /**
* Query to gather Cash states that are available and retry if they are temporarily unavailable. * Query to gather Cash states that are available and retry if they are temporarily unavailable.
@ -124,34 +125,40 @@ abstract class AbstractCashSelection {
try { try {
// we select spendable states irrespective of lock but prioritised by unlocked ones (Eg. null) // we select spendable states irrespective of lock but prioritised by unlocked ones (Eg. null)
// the softLockReserve update will detect whether we try to lock states locked by others // the softLockReserve update will detect whether we try to lock states locked by others
val rs = executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs) return executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs) { rs ->
stateAndRefs.clear() stateAndRefs.clear()
var totalPennies = 0L var totalPennies = 0L
val stateRefs = mutableSetOf<StateRef>() val stateRefs = mutableSetOf<StateRef>()
while (rs.next()) { while (rs.next()) {
val txHash = SecureHash.parse(rs.getString(1)) val txHash = SecureHash.parse(rs.getString(1))
val index = rs.getInt(2) val index = rs.getInt(2)
val pennies = rs.getLong(3) val pennies = rs.getLong(3)
totalPennies = rs.getLong(4) totalPennies = rs.getLong(4)
val rowLockId = rs.getString(5) val rowLockId = rs.getString(5)
stateRefs.add(StateRef(txHash, index)) stateRefs.add(StateRef(txHash, index))
log.trace { "ROW: $rowLockId ($lockId): ${StateRef(txHash, index)} : $pennies ($totalPennies)" } log.trace { "ROW: $rowLockId ($lockId): ${StateRef(txHash, index)} : $pennies ($totalPennies)" }
}
if (stateRefs.isNotEmpty()) {
// TODO: future implementation to retrieve contract states from a Vault BLOB store
stateAndRefs.addAll(services.loadStates(stateRefs) as Collection<StateAndRef<Cash.State>>)
}
val success = stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity
if (success) {
// we should have a minimum number of states to satisfy our selection `amount` criteria
log.trace("Coin selection for $amount retrieved ${stateAndRefs.count()} states totalling $totalPennies pennies: $stateAndRefs")
// With the current single threaded state machine available states are guaranteed to lock.
// TODO However, we will have to revisit these methods in the future multi-threaded.
services.vaultService.softLockReserve(lockId, (stateAndRefs.map { it.ref }).toNonEmptySet())
} else {
log.trace("Coin selection requested $amount but retrieved $totalPennies pennies with state refs: ${stateAndRefs.map { it.ref }}")
}
success
} }
if (stateRefs.isNotEmpty())
// TODO: future implementation to retrieve contract states from a Vault BLOB store
stateAndRefs.addAll(services.loadStates(stateRefs) as Collection<StateAndRef<Cash.State>>)
if (stateAndRefs.isNotEmpty() && totalPennies >= amount.quantity) {
// we should have a minimum number of states to satisfy our selection `amount` criteria
log.trace("Coin selection for $amount retrieved ${stateAndRefs.count()} states totalling $totalPennies pennies: $stateAndRefs")
// With the current single threaded state machine available states are guaranteed to lock.
// TODO However, we will have to revisit these methods in the future multi-threaded.
services.vaultService.softLockReserve(lockId, (stateAndRefs.map { it.ref }).toNonEmptySet())
return true
}
log.trace("Coin selection requested $amount but retrieved $totalPennies pennies with state refs: ${stateAndRefs.map { it.ref }}")
// retry as more states may become available // retry as more states may become available
} catch (e: SQLException) { } catch (e: SQLException) {
log.error("""Failed retrieving unconsumed states for: amount [$amount], onlyFromIssuerParties [$onlyFromIssuerParties], notary [$notary], lockId [$lockId] log.error("""Failed retrieving unconsumed states for: amount [$amount], onlyFromIssuerParties [$onlyFromIssuerParties], notary [$notary], lockId [$lockId]

View File

@ -30,9 +30,8 @@ class CashSelectionH2Impl : AbstractCashSelection() {
// 2) H2 uses session variables to perform this accumulator function: // 2) H2 uses session variables to perform this accumulator function:
// http://www.h2database.com/html/functions.html#set // http://www.h2database.com/html/functions.html#set
// 3) H2 does not support JOIN's in FOR UPDATE (hence we are forced to execute 2 queries) // 3) H2 does not support JOIN's in FOR UPDATE (hence we are forced to execute 2 queries)
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean {
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet { connection.createStatement().use { it.execute("CALL SET(@t, CAST(0 AS BIGINT));") }
connection.createStatement().execute("CALL SET(@t, CAST(0 AS BIGINT));")
val selectJoin = """ val selectJoin = """
SELECT vs.transaction_id, vs.output_index, ccs.pennies, SET(@t, ifnull(@t,0)+ccs.pennies) total_pennies, vs.lock_id SELECT vs.transaction_id, vs.output_index, ccs.pennies, SET(@t, ifnull(@t,0)+ccs.pennies) total_pennies, vs.lock_id
@ -50,19 +49,22 @@ class CashSelectionH2Impl : AbstractCashSelection() {
" AND ccs.issuer_ref IN (?)" else "") " AND ccs.issuer_ref IN (?)" else "")
// Use prepared statement for protection against SQL Injection (http://www.h2database.com/html/advanced.html#sql_injection) // Use prepared statement for protection against SQL Injection (http://www.h2database.com/html/advanced.html#sql_injection)
val psSelectJoin = connection.prepareStatement(selectJoin) connection.prepareStatement(selectJoin).use { psSelectJoin ->
var pIndex = 0 var pIndex = 0
psSelectJoin.setString(++pIndex, amount.token.currencyCode) psSelectJoin.setString(++pIndex, amount.token.currencyCode)
psSelectJoin.setLong(++pIndex, amount.quantity) psSelectJoin.setLong(++pIndex, amount.quantity)
psSelectJoin.setString(++pIndex, lockId.toString()) psSelectJoin.setString(++pIndex, lockId.toString())
if (notary != null) if (notary != null)
psSelectJoin.setString(++pIndex, notary.name.toString()) psSelectJoin.setString(++pIndex, notary.name.toString())
if (onlyFromIssuerParties.isNotEmpty()) if (onlyFromIssuerParties.isNotEmpty())
psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any}.toTypedArray() ) psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any }.toTypedArray())
if (withIssuerRefs.isNotEmpty()) if (withIssuerRefs.isNotEmpty())
psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray()) psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray())
log.debug { psSelectJoin.toString() } log.debug { psSelectJoin.toString() }
return psSelectJoin.executeQuery() psSelectJoin.executeQuery().use { rs ->
return withResultSet(rs)
}
}
} }
} }

View File

@ -19,7 +19,7 @@ class CashSelectionMySQLImpl : AbstractCashSelection() {
return metadata.driverName == JDBC_DRIVER_NAME return metadata.driverName == JDBC_DRIVER_NAME
} }
override fun executeQuery(statement: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, issuerKeysStr: Set<AbstractParty>, issuerRefsStr: Set<OpaqueBytes>): ResultSet { override fun executeQuery(statement: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, issuerKeysStr: Set<AbstractParty>, issuerRefsStr: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean {
TODO("MySQL cash selection not implemented") TODO("MySQL cash selection not implemented")
} }

View File

@ -27,8 +27,7 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() {
// 2) The window function accumulated column (`total`) does not include the current row (starts from 0) and cannot // 2) The window function accumulated column (`total`) does not include the current row (starts from 0) and cannot
// appear in the WHERE clause, hence restricting row selection and adjusting the returned total in the outer query. // appear in the WHERE clause, hence restricting row selection and adjusting the returned total in the outer query.
// 3) Currently (version 9.6), FOR UPDATE cannot be specified with window functions // 3) Currently (version 9.6), FOR UPDATE cannot be specified with window functions
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean {
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet {
val selectJoin = """SELECT nested.transaction_id, nested.output_index, nested.pennies, val selectJoin = """SELECT nested.transaction_id, nested.output_index, nested.pennies,
nested.total+nested.pennies as total_pennies, nested.lock_id nested.total+nested.pennies as total_pennies, nested.lock_id
FROM FROM
@ -51,29 +50,32 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() {
nested WHERE nested.total < ? nested WHERE nested.total < ?
""" """
val statement = connection.prepareStatement(selectJoin) connection.prepareStatement(selectJoin).use { statement ->
statement.setString(1, amount.token.toString()) statement.setString(1, amount.token.toString())
statement.setString(2, lockId.toString()) statement.setString(2, lockId.toString())
var paramOffset = 0 var paramOffset = 0
if (notary != null) { if (notary != null) {
statement.setString(3, notary.name.toString()) statement.setString(3, notary.name.toString())
paramOffset += 1 paramOffset += 1
} }
if (onlyFromIssuerParties.isNotEmpty()) { if (onlyFromIssuerParties.isNotEmpty()) {
val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map
{ it.owningKey.toBase58String() }.toTypedArray()) { it.owningKey.toBase58String() }.toTypedArray())
statement.setArray(3 + paramOffset, issuerKeys) statement.setArray(3 + paramOffset, issuerKeys)
paramOffset += 1 paramOffset += 1
} }
if (withIssuerRefs.isNotEmpty()) { if (withIssuerRefs.isNotEmpty()) {
val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map
{ it.bytes }.toTypedArray()) { it.bytes }.toTypedArray())
statement.setArray(3 + paramOffset, issuerRefs) statement.setArray(3 + paramOffset, issuerRefs)
paramOffset += 1 paramOffset += 1
} }
statement.setLong(3 + paramOffset, amount.quantity) statement.setLong(3 + paramOffset, amount.quantity)
log.debug { statement.toString() } log.debug { statement.toString() }
return statement.executeQuery() statement.executeQuery().use { rs ->
return withResultSet(rs)
}
}
} }
} }

View File

@ -32,34 +32,34 @@ public class CashTestsJava {
tx.input(Cash.PROGRAM_ID, inState); tx.input(Cash.PROGRAM_ID, inState);
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(Cash.PROGRAM_ID, () -> new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY()))); tw.output(Cash.PROGRAM_ID, new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY())));
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("the amounts balance"); return tw.failsWith("the amounts balance");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(Cash.PROGRAM_ID, () -> outState); tw.output(Cash.PROGRAM_ID, outState);
tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE); tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE);
// Invalid command // Invalid command
return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command"); return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(Cash.PROGRAM_ID, () -> outState); tw.output(Cash.PROGRAM_ID, outState);
tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("the owning keys are a subset of the signing keys"); return tw.failsWith("the owning keys are a subset of the signing keys");
}); });
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(Cash.PROGRAM_ID, () -> outState); tw.output(Cash.PROGRAM_ID, outState);
// issuedBy() can't be directly imported because it conflicts with other identically named functions // issuedBy() can't be directly imported because it conflicts with other identically named functions
// with different overloads (for some reason). // with different overloads (for some reason).
tw.output(Cash.PROGRAM_ID, () -> outState.issuedBy(getMINI_CORP())); tw.output(Cash.PROGRAM_ID, outState.issuedBy(getMINI_CORP()));
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.failsWith("at least one cash input"); return tw.failsWith("at least one cash input");
}); });
// Simple reallocation works. // Simple reallocation works.
return tx.tweak(tw -> { return tx.tweak(tw -> {
tw.output(Cash.PROGRAM_ID, () -> outState); tw.output(Cash.PROGRAM_ID, outState);
tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
return tw.verifies(); return tw.verifies();
}); });

View File

@ -106,8 +106,8 @@ class CommercialPaperTestsGeneric {
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
transaction("Issuance") { transaction("Issuance") {
attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract(), "paper") { thisTest.getPaper() } output(thisTest.getContract(), "paper", thisTest.getPaper())
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -118,10 +118,10 @@ class CommercialPaperTestsGeneric {
attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID) attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output(Cash.PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP } output(Cash.PROGRAM_ID, "borrowed $900", 900.DOLLARS.CASH issuedBy issuer ownedBy MEGA_CORP)
output(thisTest.getContract(), "alice's paper") { "paper".output<ICommercialPaperState>().withOwner(ALICE) } output(thisTest.getContract(), "alice's paper", "paper".output<ICommercialPaperState>().withOwner(ALICE))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand())
this.verifies() this.verifies()
} }
@ -133,13 +133,11 @@ class CommercialPaperTestsGeneric {
input("some profits") input("some profits")
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) { fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output(Cash.PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE ownedBy ALICE } output(Cash.PROGRAM_ID, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE)
output(Cash.PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP } output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP)
} }
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY))
command(ALICE_PUBKEY) { thisTest.getRedeemCommand(DUMMY_NOTARY) }
tweak { tweak {
outputs(700.DOLLARS `issued by` issuer) outputs(700.DOLLARS `issued by` issuer)
timeWindow(TEST_TX_TIME + 8.days) timeWindow(TEST_TX_TIME + 8.days)
@ -155,7 +153,7 @@ class CommercialPaperTestsGeneric {
timeWindow(TEST_TX_TIME + 8.days) timeWindow(TEST_TX_TIME + 8.days)
tweak { tweak {
output(thisTest.getContract()) { "paper".output<ICommercialPaperState>() } output(thisTest.getContract(), "paper".output<ICommercialPaperState>())
this `fails with` "must be destroyed" this `fails with` "must be destroyed"
} }
@ -169,8 +167,8 @@ class CommercialPaperTestsGeneric {
transaction { transaction {
attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract()) { thisTest.getPaper() } output(thisTest.getContract(), thisTest.getPaper())
command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
@ -181,8 +179,8 @@ class CommercialPaperTestsGeneric {
transaction { transaction {
attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer))
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
@ -193,8 +191,8 @@ class CommercialPaperTestsGeneric {
transaction { transaction {
attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days))
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "maturity date is not in the past" this `fails with` "maturity date is not in the past"
} }
@ -206,8 +204,8 @@ class CommercialPaperTestsGeneric {
attachment(CP_PROGRAM_ID) attachment(CP_PROGRAM_ID)
attachment(JavaCommercialPaper.JCP_PROGRAM_ID) attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
input(thisTest.getContract(), thisTest.getPaper()) input(thisTest.getContract(), thisTest.getPaper())
output(thisTest.getContract()) { thisTest.getPaper() } output(thisTest.getContract(), thisTest.getPaper())
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }

View File

@ -111,34 +111,33 @@ class CashTests {
fun trivial() { fun trivial() {
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
tweak { tweak {
output(Cash.PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY) { DummyCommandData } command(ALICE_PUBKEY, DummyCommandData)
// Invalid command // Invalid command
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
} }
tweak { tweak {
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(BOB_PUBKEY) { Cash.Commands.Move() } command(BOB_PUBKEY, Cash.Commands.Move())
this `fails with` "the owning keys are a subset of the signing keys" this `fails with` "the owning keys are a subset of the signing keys"
} }
tweak { tweak {
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "at least one cash input" this `fails with` "at least one cash input"
} }
// Simple reallocation works. // Simple reallocation works.
tweak { tweak {
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -149,10 +148,9 @@ class CashTests {
// Check we can't "move" money into existence. // Check we can't "move" money into existence.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { DummyState() } input(Cash.PROGRAM_ID, DummyState())
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } command(MINI_CORP_PUBKEY, Cash.Commands.Move())
this `fails with` "there is at least one cash input for this group" this `fails with` "there is at least one cash input for this group"
} }
} }
@ -163,19 +161,17 @@ class CashTests {
// institution is allowed to issue as much cash as they want. // institution is allowed to issue as much cash as they want.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY) { Cash.Commands.Issue() } command(ALICE_PUBKEY, Cash.Commands.Issue())
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID) { output(Cash.PROGRAM_ID,
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
owner = AnonymousParty(ALICE_PUBKEY) owner = AnonymousParty(ALICE_PUBKEY)))
) command(MINI_CORP_PUBKEY, Cash.Commands.Issue())
}
command(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
this.verifies() this.verifies()
} }
} }
@ -211,18 +207,17 @@ class CashTests {
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { issuerInState } input(Cash.PROGRAM_ID, issuerInState)
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
// Move fails: not allowed to summon money. // Move fails: not allowed to summon money.
tweak { tweak {
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Issue works. // Issue works.
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
this.verifies() this.verifies()
} }
} }
@ -230,29 +225,29 @@ class CashTests {
// Can't use an issue command to lower the amount. // Can't use an issue command to lower the amount.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) } output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first()))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have an issue command that doesn't actually issue money. // Can't have an issue command that doesn't actually issue money.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { inState } output(Cash.PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have any other commands if we have an issue command (because the issue command overrules them) // Can't have any other commands if we have an issue command (because the issue command overrules them)
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
this `fails with` "there is only a single issue command" this `fails with` "there is only a single issue command"
} }
this.verifies() this.verifies()
@ -282,26 +277,26 @@ class CashTests {
// Splitting value works. // Splitting value works.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
tweak { tweak {
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
val splits4 = inState.amount.splitEvenly(4) val splits4 = inState.amount.splitEvenly(4)
for (i in 0..3) output(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } for (i in 0..3) output(Cash.PROGRAM_ID, inState.copy(amount = splits4[i]))
this.verifies() this.verifies()
} }
// Merging 4 inputs into 2 outputs works. // Merging 4 inputs into 2 outputs works.
tweak { tweak {
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
val splits4 = inState.amount.splitEvenly(4) val splits4 = inState.amount.splitEvenly(4)
for (i in 0..3) input(Cash.PROGRAM_ID) { inState.copy(amount = splits4[i]) } for (i in 0..3) input(Cash.PROGRAM_ID, inState.copy(amount = splits4[i]))
for (i in 0..1) output(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } for (i in 0..1) output(Cash.PROGRAM_ID, inState.copy(amount = splits2[i]))
this.verifies() this.verifies()
} }
// Merging 2 inputs into 1 works. // Merging 2 inputs into 1 works.
tweak { tweak {
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
for (i in 0..1) input(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) } for (i in 0..1) input(Cash.PROGRAM_ID, inState.copy(amount = splits2[i]))
output(Cash.PROGRAM_ID) { inState } output(Cash.PROGRAM_ID, inState)
this.verifies() this.verifies()
} }
} }
@ -311,17 +306,17 @@ class CashTests {
fun zeroSizedValues() { fun zeroSizedValues() {
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "zero sized inputs" this `fails with` "zero sized inputs"
} }
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { inState } output(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "zero sized outputs" this `fails with` "zero sized outputs"
} }
} }
@ -331,58 +326,56 @@ class CashTests {
// Can't change issuer. // Can't change issuer.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP } output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't change deposit reference when splitting. // Can't change deposit reference when splitting.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
val splits2 = inState.amount.splitEvenly(2) val splits2 = inState.amount.splitEvenly(2)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
for (i in 0..1) output(Cash.PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte()))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't mix currencies. // Can't mix currencies.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer))
output(Cash.PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID) { input(Cash.PROGRAM_ID,
inState.copy( inState.copy(
amount = 150.POUNDS `issued by` defaultIssuer, amount = 150.POUNDS `issued by` defaultIssuer,
owner = AnonymousParty(BOB_PUBKEY) owner = AnonymousParty(BOB_PUBKEY)))
) output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer))
} command(ALICE_PUBKEY, Cash.Commands.Move())
output(Cash.PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
command(ALICE_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't have superfluous input states from different issuers. // Can't have superfluous input states from different issuers.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
output(Cash.PROGRAM_ID) { outState } output(Cash.PROGRAM_ID, outState)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't combine two different deposits at the same issuer. // Can't combine two different deposits at the same issuer.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID) { inState.editDepositRef(3) } input(Cash.PROGRAM_ID, inState.editDepositRef(3))
output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) } output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "for reference [01]" this `fails with` "for reference [01]"
} }
} }
@ -392,21 +385,20 @@ class CashTests {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { issuerInState } input(Cash.PROGRAM_ID, issuerInState)
output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) } command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -418,20 +410,15 @@ class CashTests {
// Multi-issuer case. // Multi-issuer case.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { issuerInState } input(Cash.PROGRAM_ID, issuerInState)
input(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP } input(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP) issuedBy MINI_CORP)
output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP)
output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) issuedBy MINI_CORP } output(Cash.PROGRAM_ID, issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
output(Cash.PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move())
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(MINI_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef)))
command(MINI_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` MINI_CORP.ref(defaultRef)) }
this.verifies() this.verifies()
} }
} }
@ -441,10 +428,10 @@ class CashTests {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
} }
@ -454,25 +441,24 @@ class CashTests {
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
// Gather 2000 dollars from two different issuers. // Gather 2000 dollars from two different issuers.
input(Cash.PROGRAM_ID) { inState } input(Cash.PROGRAM_ID, inState)
input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP } input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY, Cash.Commands.Move())
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output(Cash.PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer))
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output(Cash.PROGRAM_ID) { 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)) } output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)))
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output(Cash.PROGRAM_ID) { 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)) issuedBy MINI_CORP } output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP)
this.verifies() this.verifies()
} }
} }
@ -483,12 +469,11 @@ class CashTests {
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY))
input(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(ALICE_PUBKEY) } input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY))
input(Cash.PROGRAM_ID) { pounds } input(Cash.PROGRAM_ID, pounds)
output(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(BOB_PUBKEY) } output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY))
output(Cash.PROGRAM_ID) { pounds ownedBy AnonymousParty(ALICE_PUBKEY) } output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY))
command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -792,19 +777,17 @@ class CashTests {
ledger(mockService) { ledger(mockService) {
unverifiedTransaction { unverifiedTransaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
output(Cash.PROGRAM_ID, "MEGA_CORP cash") { output(Cash.PROGRAM_ID, "MEGA_CORP cash",
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP owner = MEGA_CORP))
)
}
} }
transaction { transaction {
attachment(Cash.PROGRAM_ID) attachment(Cash.PROGRAM_ID)
input("MEGA_CORP cash") input("MEGA_CORP cash")
output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(ALICE_PUBKEY))) output(Cash.PROGRAM_ID, "MEGA_CORP cash 2", "MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(ALICE_PUBKEY)))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
this.verifies() this.verifies()
} }
@ -814,7 +797,7 @@ class CashTests {
input("MEGA_CORP cash") input("MEGA_CORP cash")
// We send it to another pubkey so that the transaction is not identical to the previous one // We send it to another pubkey so that the transaction is not identical to the previous one
output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE)) output(Cash.PROGRAM_ID, "MEGA_CORP cash 3", "MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
this.verifies() this.verifies()
} }
this.fails() this.fails()

View File

@ -71,34 +71,33 @@ class ObligationTests {
fun trivial() { fun trivial() {
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
tweak { tweak {
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) } output(Obligation.PROGRAM_ID, outState.copy(quantity = 2000.DOLLARS.quantity))
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(CHARLIE.owningKey) { DummyCommandData } command(CHARLIE.owningKey, DummyCommandData)
// Invalid command // Invalid command
this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command"
} }
tweak { tweak {
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(BOB_PUBKEY) { Obligation.Commands.Move() } command(BOB_PUBKEY, Obligation.Commands.Move())
this `fails with` "the owning keys are a subset of the signing keys" this `fails with` "the owning keys are a subset of the signing keys"
} }
tweak { tweak {
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP } output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "at least one obligation input" this `fails with` "at least one obligation input"
} }
// Simple reallocation works. // Simple reallocation works.
tweak { tweak {
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -109,10 +108,9 @@ class ObligationTests {
// Check we can't "move" debt into existence. // Check we can't "move" debt into existence.
transaction { transaction {
attachments(DummyContract.PROGRAM_ID, Obligation.PROGRAM_ID) attachments(DummyContract.PROGRAM_ID, Obligation.PROGRAM_ID)
input(DummyContract.PROGRAM_ID) { DummyState() } input(DummyContract.PROGRAM_ID, DummyState())
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
this `fails with` "at least one obligation input" this `fails with` "at least one obligation input"
} }
@ -120,21 +118,19 @@ class ObligationTests {
// institution is allowed to issue as much cash as they want. // institution is allowed to issue as much cash as they want.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(CHARLIE.owningKey) { Obligation.Commands.Issue() } command(CHARLIE.owningKey, Obligation.Commands.Issue())
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
output(Obligation.PROGRAM_ID) { output(Obligation.PROGRAM_ID,
Obligation.State( Obligation.State(
obligor = MINI_CORP, obligor = MINI_CORP,
quantity = 1000.DOLLARS.quantity, quantity = 1000.DOLLARS.quantity,
beneficiary = CHARLIE, beneficiary = CHARLIE,
template = megaCorpDollarSettlement template = megaCorpDollarSettlement))
) command(MINI_CORP_PUBKEY, Obligation.Commands.Issue())
}
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue() }
this.verifies() this.verifies()
} }
run { run {
@ -157,18 +153,17 @@ class ObligationTests {
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2))
// Move fails: not allowed to summon money. // Move fails: not allowed to summon money.
tweak { tweak {
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Issue works. // Issue works.
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
this.verifies() this.verifies()
} }
} }
@ -176,29 +171,29 @@ class ObligationTests {
// Can't use an issue command to lower the amount. // Can't use an issue command to lower the amount.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) } output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity / 2))
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
this `fails with` "output values sum to more than the inputs" this `fails with` "output values sum to more than the inputs"
} }
// Can't have an issue command that doesn't actually issue money. // Can't have an issue command that doesn't actually issue money.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState } output(Obligation.PROGRAM_ID, inState)
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
this `fails with` "" this `fails with` ""
} }
// Can't have any other commands if we have an issue command (because the issue command overrules them). // Can't have any other commands if we have an issue command (because the issue command overrules them).
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2))
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
tweak { tweak {
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
this `fails with` "there is only a single issue command" this `fails with` "there is only a single issue command"
} }
this.verifies() this.verifies()
@ -352,7 +347,7 @@ class ObligationTests {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
// Note we can sign with either key here // Note we can sign with either key here
command(ALICE_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(ALICE_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -368,8 +363,8 @@ class ObligationTests {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output(Obligation.PROGRAM_ID, "change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) } output(Obligation.PROGRAM_ID, "change", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB))
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(listOf(BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.CLOSE_OUT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -383,8 +378,8 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
output(Obligation.PROGRAM_ID, "change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) } output(Obligation.PROGRAM_ID, "change", oneMillionDollars.splitEvenly(2).first().OBLIGATION between Pair(ALICE, BOB))
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "amounts owed on input and output must match" this `fails with` "amounts owed on input and output must match"
} }
@ -397,7 +392,7 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(MEGA_CORP_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "any involved party has signed" this `fails with` "any involved party has signed"
} }
@ -413,7 +408,7 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -428,7 +423,7 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(BOB_PUBKEY, Obligation.Commands.Net(NetType.PAYMENT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "all involved parties have signed" this `fails with` "all involved parties have signed"
} }
@ -441,8 +436,8 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE))
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(listOf(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -456,8 +451,8 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output(Obligation.PROGRAM_ID, "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) { Obligation.Commands.Net(NetType.PAYMENT) } command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "all involved parties have signed" this `fails with` "all involved parties have signed"
} }
@ -473,9 +468,9 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB)
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)))
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java))
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
this.verifies() this.verifies()
} }
@ -488,10 +483,10 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID) attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID)
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
input(Cash.PROGRAM_ID, 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE) input(Cash.PROGRAM_ID, 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE)
output(Obligation.PROGRAM_ID, "Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) } output(Obligation.PROGRAM_ID, "Alice's $500,000 obligation to Bob", halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB))
output(Obligation.PROGRAM_ID, "Bob's $500,000") { 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } output(Obligation.PROGRAM_ID, "Bob's $500,000", 500000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB)
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)))
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java))
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
this.verifies() this.verifies()
} }
@ -504,9 +499,9 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID) attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID)
input(Obligation.PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob input(Obligation.PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
input(Cash.PROGRAM_ID, 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE) input(Cash.PROGRAM_ID, 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy ALICE)
output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB)
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)))
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java))
this `fails with` "all inputs are in the normal state" this `fails with` "all inputs are in the normal state"
} }
} }
@ -518,9 +513,9 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output(Obligation.PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB } output(Obligation.PROGRAM_ID, "Bob's $1,000,000", 1000000.DOLLARS.CASH issuedBy defaultIssuer ownedBy BOB)
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)))
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java))
attachment(attachment(cashContractBytes.inputStream())) attachment(attachment(cashContractBytes.inputStream()))
this `fails with` "amount in settle command" this `fails with` "amount in settle command"
} }
@ -546,9 +541,9 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's 1 FCOJ obligation to Bob") input("Alice's 1 FCOJ obligation to Bob")
input("Alice's 1 FCOJ") input("Alice's 1 FCOJ")
output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) } output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ", CommodityContract.State(oneUnitFcoj, BOB))
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) } command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)))
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) } command(ALICE_PUBKEY, CommodityContract.Commands.Move(Obligation::class.java))
attachment(attachment(commodityContractBytes.inputStream())) attachment(attachment(commodityContractBytes.inputStream()))
verifies() verifies()
} }
@ -563,8 +558,8 @@ class ObligationTests {
transaction("Settlement") { transaction("Settlement") {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) } output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED))
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
this `fails with` "there is a time-window from the authority" this `fails with` "there is a time-window from the authority"
} }
} }
@ -575,8 +570,8 @@ class ObligationTests {
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) 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) } output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED))
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "the due date has passed" this `fails with` "the due date has passed"
} }
@ -586,8 +581,8 @@ class ObligationTests {
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) 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) } output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED))
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -600,24 +595,24 @@ class ObligationTests {
// Splitting value works. // Splitting value works.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
tweak { tweak {
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
repeat(4) { output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } repeat(4) { output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 4)) }
this.verifies() this.verifies()
} }
// Merging 4 inputs into 2 outputs works. // Merging 4 inputs into 2 outputs works.
tweak { tweak {
repeat(4) { input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } 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))
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
this.verifies() this.verifies()
} }
// Merging 2 inputs into 1 works. // Merging 2 inputs into 1 works.
tweak { tweak {
input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
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 } output(Obligation.PROGRAM_ID, inState)
this.verifies() this.verifies()
} }
} }
@ -627,18 +622,16 @@ class ObligationTests {
fun zeroSizedValues() { fun zeroSizedValues() {
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
tweak { tweak {
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
input(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) } input(Obligation.PROGRAM_ID, inState.copy(quantity = 0L))
this `fails with` "zero sized inputs" this `fails with` "zero sized inputs"
} }
tweak { tweak {
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState } output(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) } output(Obligation.PROGRAM_ID, inState.copy(quantity = 0L))
this `fails with` "zero sized outputs" this `fails with` "zero sized outputs"
} }
} }
@ -649,41 +642,39 @@ class ObligationTests {
// Can't change issuer. // Can't change issuer.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP } output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP)
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't mix currencies. // Can't mix currencies.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } output(Obligation.PROGRAM_ID, outState.copy(quantity = 80000, template = megaCorpDollarSettlement))
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } output(Obligation.PROGRAM_ID, outState.copy(quantity = 20000, template = megaCorpPoundSettlement))
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
input(Obligation.PROGRAM_ID) { input(Obligation.PROGRAM_ID,
inState.copy( inState.copy(
quantity = 15000, quantity = 15000,
template = megaCorpPoundSettlement, template = megaCorpPoundSettlement,
beneficiary = AnonymousParty(BOB_PUBKEY) beneficiary = AnonymousParty(BOB_PUBKEY)))
) output(Obligation.PROGRAM_ID, outState.copy(quantity = 115000))
} command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 115000) }
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Can't have superfluous input states from different issuers. // Can't have superfluous input states from different issuers.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP } input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP)
output(Obligation.PROGRAM_ID) { outState } output(Obligation.PROGRAM_ID, outState)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
} }
@ -693,21 +684,20 @@ class ObligationTests {
// Single input/output straightforward case. // Single input/output straightforward case.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
output(Obligation.PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } output(Obligation.PROGRAM_ID, outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity))
tweak { tweak {
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)))
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) } command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)))
this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command"
tweak { tweak {
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -720,21 +710,15 @@ class ObligationTests {
// Multi-product case. // Multi-product case.
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)))
input(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) } input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)))
input(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) } 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))
output(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } command(CHARLIE.owningKey, Obligation.Commands.Move())
output(Obligation.PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))))
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))))
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) }
this.verifies() this.verifies()
} }
} }
@ -745,27 +729,26 @@ class ObligationTests {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
// Gather 2000 dollars from two different issuers. // Gather 2000 dollars from two different issuers.
input(Obligation.PROGRAM_ID) { inState } input(Obligation.PROGRAM_ID, inState)
input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP } input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP)
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) } output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L))
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output(Obligation.PROGRAM_ID) { 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)) } output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)))
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output(Obligation.PROGRAM_ID) { 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)) `issued by` MINI_CORP } output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP)
command(CHARLIE.owningKey) { Obligation.Commands.Move() } command(CHARLIE.owningKey, Obligation.Commands.Move())
this.verifies() this.verifies()
} }
} }
@ -776,12 +759,11 @@ class ObligationTests {
transaction { transaction {
attachments(Obligation.PROGRAM_ID) attachments(Obligation.PROGRAM_ID)
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY)) val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY))
input(Obligation.PROGRAM_ID) { inState `owned by` CHARLIE } input(Obligation.PROGRAM_ID, inState `owned by` CHARLIE)
input(Obligation.PROGRAM_ID) { pounds } input(Obligation.PROGRAM_ID, pounds)
output(Obligation.PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } output(Obligation.PROGRAM_ID, inState `owned by` AnonymousParty(BOB_PUBKEY))
output(Obligation.PROGRAM_ID) { pounds `owned by` CHARLIE } output(Obligation.PROGRAM_ID, pounds `owned by` CHARLIE)
command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() } command(listOf(CHARLIE.owningKey, BOB_PUBKEY), Obligation.Commands.Move())
this.verifies() this.verifies()
} }
} }

View File

@ -3,7 +3,8 @@ package net.corda.nodeapi
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.requireOnDefaultFileSystem
import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.api.core.TransportConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants

View File

@ -1,6 +1,6 @@
@file:JvmName("ArtemisUtils") @file:JvmName("ArtemisUtils")
package net.corda.nodeapi package net.corda.nodeapi.internal
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path import java.nio.file.Path

View File

@ -1,6 +1,6 @@
@file:JvmName("ConfigUtilities") @file:JvmName("ConfigUtilities")
package net.corda.nodeapi.config package net.corda.nodeapi.internal.config
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
@ -200,4 +200,4 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
} }
} }
private val logger = LoggerFactory.getLogger("net.corda.nodeapi.config") private val logger = LoggerFactory.getLogger("net.corda.nodeapi.internal.config")

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.config package net.corda.nodeapi.internal.config
import net.corda.core.internal.div import net.corda.core.internal.div
import java.nio.file.Path import java.nio.file.Path

View File

@ -1,7 +1,4 @@
package net.corda.nodeapi package net.corda.nodeapi.internal.config
import net.corda.nodeapi.config.OldConfig
import net.corda.nodeapi.config.toConfig
data class User( data class User(
@OldConfig("user") @OldConfig("user")

View File

@ -1,4 +1,4 @@
package net.corda.nodeapi.config package net.corda.nodeapi.internal.config
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory.empty import com.typesafe.config.ConfigFactory.empty

View File

@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import net.corda.testing.driver.driver import net.corda.testing.driver.driver

View File

@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity

View File

@ -13,7 +13,7 @@ import net.corda.finance.DOLLARS
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver

View File

@ -9,7 +9,7 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.bouncycastle.util.io.Streams import org.bouncycastle.util.io.Streams

View File

@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.driver import net.corda.testing.driver.driver

View File

@ -49,7 +49,7 @@ class NodeInfoWatcherTest {
fun start() { fun start() {
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
keyManagementService = MockKeyManagementService(identityService, ALICE_KEY) keyManagementService = MockKeyManagementService(identityService, ALICE_KEY)
nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler = scheduler) nodeInfoWatcher = NodeInfoWatcher(tempFolder.root.toPath(), scheduler)
nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY nodeInfoPath = tempFolder.root.toPath() / CordformNode.NODE_INFO_DIRECTORY
} }

View File

@ -4,15 +4,15 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.internal.InputStreamAndHash import net.corda.core.internal.InputStreamAndHash
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.testing.BOB import net.corda.core.utilities.getOrThrow
import net.corda.testing.DUMMY_NOTARY import net.corda.nodeapi.internal.config.User
import net.corda.testing.aliceAndBob import net.corda.testing.*
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.dummyCommand
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -65,15 +65,16 @@ class LargeTransactionsTest {
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2) val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3) val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) { driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
val (alice, _) = aliceAndBob() val rpcUser = User("admin", "admin", setOf("ALL"))
alice.useRPC { val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow()
val hash1 = it.uploadAttachment(bigFile1.inputStream) alice.rpcClientToNode().use(rpcUser.username, rpcUser.password) {
val hash2 = it.uploadAttachment(bigFile2.inputStream) val hash1 = it.proxy.uploadAttachment(bigFile1.inputStream)
val hash3 = it.uploadAttachment(bigFile3.inputStream) val hash2 = it.proxy.uploadAttachment(bigFile2.inputStream)
val hash4 = it.uploadAttachment(bigFile4.inputStream) val hash3 = it.proxy.uploadAttachment(bigFile3.inputStream)
val hash4 = it.proxy.uploadAttachment(bigFile4.inputStream)
assertEquals(hash1, bigFile1.sha256) assertEquals(hash1, bigFile1.sha256)
// Should not throw any exceptions. // Should not throw any exceptions.
it.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.get() it.proxy.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.getOrThrow()
} }
} }
} }

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.*
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP

View File

@ -1,6 +1,6 @@
package net.corda.services.messaging package net.corda.services.messaging
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient
import org.junit.Test import org.junit.Test

View File

@ -22,8 +22,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATI
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREFIX
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.internal.NodeBasedTest import net.corda.testing.internal.NodeBasedTest
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient

View File

@ -19,7 +19,7 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentity
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Assume.assumeFalse import org.junit.Assume.assumeFalse

View File

@ -66,6 +66,7 @@ import org.apache.activemq.artemis.utils.ReusableLatch
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
import rx.Scheduler
import java.io.IOException import java.io.IOException
import java.io.NotSerializableException import java.io.NotSerializableException
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@ -232,7 +233,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
} }
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache, val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
NodeInfoWatcher(configuration.baseDirectory, Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)), NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
networkMapClient) networkMapClient)
runOnStop += networkMapUpdater::close runOnStop += networkMapUpdater::close
@ -258,6 +259,12 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
} }
} }
/**
* Should be [rx.schedulers.Schedulers.io] for production,
* or [rx.internal.schedulers.CachedThreadScheduler] (with shutdown registered with [runOnStop]) for shared-JVM testing.
*/
protected abstract fun getRxIoScheduler(): Scheduler
open fun startShell(rpcOps: CordaRPCOps) { open fun startShell(rpcOps: CordaRPCOps) {
InteractiveShell.startShell(configuration, rpcOps, userService, _services.identityService, _services.database) InteractiveShell.startShell(configuration, rpcOps, userService, _services.identityService, _services.database)
} }

View File

@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import rx.schedulers.Schedulers
import java.time.Clock import java.time.Clock
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.management.ObjectName import javax.management.ObjectName
@ -46,7 +47,7 @@ import kotlin.system.exitProcess
*/ */
open class Node(configuration: NodeConfiguration, open class Node(configuration: NodeConfiguration,
versionInfo: VersionInfo, versionInfo: VersionInfo,
val initialiseSerialization: Boolean = true, private val initialiseSerialization: Boolean = true,
cordappLoader: CordappLoader = makeCordappLoader(configuration) cordappLoader: CordappLoader = makeCordappLoader(configuration)
) : AbstractNode(configuration, createClock(configuration), versionInfo, cordappLoader) { ) : AbstractNode(configuration, createClock(configuration), versionInfo, cordappLoader) {
companion object { companion object {
@ -299,6 +300,7 @@ open class Node(configuration: NodeConfiguration,
return started return started
} }
override fun getRxIoScheduler() = Schedulers.io()!!
private fun initialiseSerialization() { private fun initialiseSerialization() {
val classloader = cordappLoader.appClassLoader val classloader = cordappLoader.appClassLoader
nodeSerializationEnv = SerializationEnvironmentImpl( nodeSerializationEnv = SerializationEnvironmentImpl(

View File

@ -1,7 +1,7 @@
package net.corda.node.services package net.corda.node.services
import net.corda.core.context.AuthServiceId import net.corda.core.context.AuthServiceId
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
/** /**
* Service for retrieving [User] objects representing RPC users who are authorised to use the RPC system. A [User] * Service for retrieving [User] objects representing RPC users who are authorised to use the RPC system. A [User]

View File

@ -8,7 +8,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.SignatureScheme
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.crypto.* import net.corda.nodeapi.internal.crypto.*
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree

View File

@ -6,9 +6,9 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.node.services.messaging.CertificateChainCheckPolicy import net.corda.node.services.messaging.CertificateChainCheckPolicy
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.internal.config.parseAs
import java.net.URL import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*

View File

@ -6,7 +6,7 @@ import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE

View File

@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisPeerAddress import net.corda.nodeapi.internal.ArtemisMessagingComponent.ArtemisPeerAddress
import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress import net.corda.nodeapi.internal.ArtemisMessagingComponent.NodeAddress
import net.corda.nodeapi.internal.requireOnDefaultFileSystem
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
import org.apache.activemq.artemis.core.config.BridgeConfiguration import org.apache.activemq.artemis.core.config.BridgeConfiguration
@ -43,7 +44,9 @@ import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration
import org.apache.activemq.artemis.core.remoting.impl.netty.* import org.apache.activemq.artemis.core.remoting.impl.netty.*
import org.apache.activemq.artemis.core.security.Role import org.apache.activemq.artemis.core.security.Role
import org.apache.activemq.artemis.core.server.ActiveMQServer import org.apache.activemq.artemis.core.server.ActiveMQServer
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
import org.apache.activemq.artemis.core.settings.HierarchicalRepository
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
import org.apache.activemq.artemis.core.settings.impl.AddressSettings import org.apache.activemq.artemis.core.settings.impl.AddressSettings
import org.apache.activemq.artemis.spi.core.remoting.* import org.apache.activemq.artemis.spi.core.remoting.*
@ -139,8 +142,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
// Artemis IO errors // Artemis IO errors
@Throws(IOException::class, KeyStoreException::class) @Throws(IOException::class, KeyStoreException::class)
private fun configureAndStartServer() { private fun configureAndStartServer() {
val artemisConfig = createArtemisConfig() val (artemisConfig, securityPlugin) = createArtemisConfig()
val securityManager = createArtemisSecurityManager() val securityManager = createArtemisSecurityManager(securityPlugin)
activeMQServer = ActiveMQServerImpl(artemisConfig, securityManager).apply { activeMQServer = ActiveMQServerImpl(artemisConfig, securityManager).apply {
// Throw any exceptions which are detected during startup // Throw any exceptions which are detected during startup
registerActivationFailureListener { exception -> throw exception } registerActivationFailureListener { exception -> throw exception }
@ -156,7 +159,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
} }
} }
private fun createArtemisConfig(): Configuration = ConfigurationImpl().apply { private fun createArtemisConfig() = ConfigurationImpl().apply {
val artemisDir = config.baseDirectory / "artemis" val artemisDir = config.baseDirectory / "artemis"
bindingsDirectory = (artemisDir / "bindings").toString() bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString() journalDirectory = (artemisDir / "journal").toString()
@ -208,8 +211,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
} }
) )
configureAddressSecurity() }.configureAddressSecurity()
}
private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration { private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration {
return CoreQueueConfiguration().apply { return CoreQueueConfiguration().apply {
@ -227,7 +229,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
* 3. RPC users. These are only given sufficient access to perform RPC with us. * 3. RPC users. These are only given sufficient access to perform RPC with us.
* 4. Verifiers. These are given read access to the verification request queue and write access to the response queue. * 4. Verifiers. These are given read access to the verification request queue and write access to the response queue.
*/ */
private fun ConfigurationImpl.configureAddressSecurity() { private fun ConfigurationImpl.configureAddressSecurity() : Pair<Configuration, LoginListener> {
val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true) val nodeInternalRole = Role(NODE_ROLE, true, true, true, true, true, true, true, true)
securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node securityRoles["$INTERNAL_PREFIX#"] = setOf(nodeInternalRole) // Do not add any other roles here as it's only for the node
securityRoles[P2P_QUEUE] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true)) securityRoles[P2P_QUEUE] = setOf(nodeInternalRole, restrictedRole(PEER_ROLE, send = true))
@ -236,13 +238,22 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$NODE_USER.#"] = setOf(nodeInternalRole) securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$NODE_USER.#"] = setOf(nodeInternalRole)
// Each RPC user must have its own role and its own queue. This prevents users accessing each other's queues // Each RPC user must have its own role and its own queue. This prevents users accessing each other's queues
// and stealing RPC responses. // and stealing RPC responses.
for ((username) in userService.users) { val rolesAdderOnLogin = RolesAdderOnLogin { username ->
securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#"] = setOf( Pair(
nodeInternalRole, "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#",
restrictedRole("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", consume = true, createNonDurableQueue = true, deleteNonDurableQueue = true)) setOf(
nodeInternalRole,
restrictedRole(
"${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username",
consume = true,
createNonDurableQueue = true,
deleteNonDurableQueue = true)))
} }
securitySettingPlugins.add(rolesAdderOnLogin)
securityRoles[VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, consume = true)) securityRoles[VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, consume = true))
securityRoles["${VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX}.#"] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, send = true)) securityRoles["${VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX}.#"] = setOf(nodeInternalRole, restrictedRole(VERIFIER_ROLE, send = true))
val onLoginListener = { username: String -> rolesAdderOnLogin.onLogin(username) }
return Pair(this, onLoginListener)
} }
private fun restrictedRole(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, private fun restrictedRole(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false,
@ -253,7 +264,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
} }
@Throws(IOException::class, KeyStoreException::class) @Throws(IOException::class, KeyStoreException::class)
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager { private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager {
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword) val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword) val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
@ -270,6 +281,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
// Override to make it work with our login module // Override to make it work with our login module
override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> { override fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> {
val options = mapOf( val options = mapOf(
LoginListener::javaClass.name to loginListener,
RPCUserService::class.java.name to userService, RPCUserService::class.java.name to userService,
NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks) NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks)
return arrayOf(AppConfigurationEntry(name, REQUIRED, options)) return arrayOf(AppConfigurationEntry(name, REQUIRED, options))
@ -546,6 +558,7 @@ class NodeLoginModule : LoginModule {
private lateinit var subject: Subject private lateinit var subject: Subject
private lateinit var callbackHandler: CallbackHandler private lateinit var callbackHandler: CallbackHandler
private lateinit var userService: RPCUserService private lateinit var userService: RPCUserService
private lateinit var loginListener: LoginListener
private lateinit var peerCertCheck: CertificateChainCheckPolicy.Check private lateinit var peerCertCheck: CertificateChainCheckPolicy.Check
private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check
private lateinit var verifierCertCheck: CertificateChainCheckPolicy.Check private lateinit var verifierCertCheck: CertificateChainCheckPolicy.Check
@ -555,6 +568,7 @@ class NodeLoginModule : LoginModule {
this.subject = subject this.subject = subject
this.callbackHandler = callbackHandler this.callbackHandler = callbackHandler
userService = options[RPCUserService::class.java.name] as RPCUserService userService = options[RPCUserService::class.java.name] as RPCUserService
loginListener = options[LoginListener::javaClass.name] as LoginListener
val certChainChecks: Map<String, CertificateChainCheckPolicy.Check> = uncheckedCast(options[CERT_CHAIN_CHECKS_OPTION_NAME]) val certChainChecks: Map<String, CertificateChainCheckPolicy.Check> = uncheckedCast(options[CERT_CHAIN_CHECKS_OPTION_NAME])
peerCertCheck = certChainChecks[PEER_ROLE]!! peerCertCheck = certChainChecks[PEER_ROLE]!!
nodeCertCheck = certChainChecks[NODE_ROLE]!! nodeCertCheck = certChainChecks[NODE_ROLE]!!
@ -622,6 +636,7 @@ class NodeLoginModule : LoginModule {
// TODO Retrieve client IP address to include in exception message // TODO Retrieve client IP address to include in exception message
throw FailedLoginException("Password for user $username does not match") throw FailedLoginException("Password for user $username does not match")
} }
loginListener(username)
principals += RolePrincipal(RPC_ROLE) // This enables the RPC client to send requests principals += RolePrincipal(RPC_ROLE) // This enables the RPC client to send requests
principals += RolePrincipal("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses principals += RolePrincipal("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses
return username return username
@ -676,3 +691,40 @@ class NodeLoginModule : LoginModule {
loginSucceeded = false loginSucceeded = false
} }
} }
typealias LoginListener = (String) -> Unit
typealias RolesRepository = HierarchicalRepository<MutableSet<Role>>
/**
* Helper class to dynamically assign security roles to RPC users
* on their authentication. This object is plugged into the server
* as [SecuritySettingPlugin]. It responds to authentication events
* from [NodeLoginModule] by adding the address -> roles association
* generated by the given [source], unless already done before.
*/
private class RolesAdderOnLogin(val source: (String) -> Pair<String, Set<Role>>)
: SecuritySettingPlugin {
// Artemis internal container storing roles association
private lateinit var repository: RolesRepository
fun onLogin(username: String) {
val (address, roles) = source(username)
val entry = repository.getMatch(address)
if (entry == null || entry.isEmpty()) {
repository.addMatch(address, roles.toMutableSet())
}
}
// Initializer called by the Artemis framework
override fun setSecurityRepository(repository: RolesRepository) {
this.repository = repository
}
// Part of SecuritySettingPlugin interface which is no-op in this case
override fun stop() = this
override fun init(options: MutableMap<String, String>?) = this
override fun getSecurityRoles() = null
}

View File

@ -5,7 +5,7 @@ import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserService
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.getX509Certificate import net.corda.nodeapi.internal.crypto.getX509Certificate

View File

@ -30,6 +30,7 @@ import net.corda.node.services.RPCUserService
import net.corda.node.services.logging.pushToLoggingContext import net.corda.node.services.logging.pushToLoggingContext
import net.corda.nodeapi.* import net.corda.nodeapi.*
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.config.User
import org.apache.activemq.artemis.api.core.Message import org.apache.activemq.artemis.api.core.Message
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE

View File

@ -11,7 +11,7 @@ import net.corda.node.utilities.*
import net.corda.nodeapi.VerifierApi import net.corda.nodeapi.VerifierApi
import net.corda.nodeapi.VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME import net.corda.nodeapi.VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME
import net.corda.nodeapi.VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX import net.corda.nodeapi.VerifierApi.VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*

View File

@ -12,7 +12,6 @@ import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.NodeInfoFilesCopier import net.corda.nodeapi.internal.NodeInfoFilesCopier
import rx.Observable import rx.Observable
import rx.Scheduler import rx.Scheduler
import rx.schedulers.Schedulers
import java.io.IOException import java.io.IOException
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
@ -31,9 +30,8 @@ import kotlin.streams.toList
*/ */
// TODO: Use NIO watch service instead? // TODO: Use NIO watch service instead?
class NodeInfoWatcher(private val nodePath: Path, class NodeInfoWatcher(private val nodePath: Path,
private val pollInterval: Duration = 5.seconds, private val scheduler: Scheduler,
private val scheduler: Scheduler = Schedulers.io()) { private val pollInterval: Duration = 5.seconds) {
private val nodeInfoDirectory = nodePath / CordformNode.NODE_INFO_DIRECTORY private val nodeInfoDirectory = nodePath / CordformNode.NODE_INFO_DIRECTORY
private val processedNodeInfoFiles = mutableSetOf<Path>() private val processedNodeInfoFiles = mutableSetOf<Path>()
private val _processedNodeInfoHashes = mutableSetOf<SecureHash>() private val _processedNodeInfoHashes = mutableSetOf<SecureHash>()

View File

@ -29,8 +29,8 @@ import net.corda.core.serialization.serialize
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.services.config.RaftConfig import net.corda.node.services.config.RaftConfig
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import java.nio.file.Path import java.nio.file.Path

View File

@ -653,13 +653,13 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
// wants to sell to Bob. // wants to sell to Bob.
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
// Issued money to itself. // Issued money to itself.
output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } output(Cash.PROGRAM_ID, "elbonian money 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy interimOwner)
output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary) { 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } output(Cash.PROGRAM_ID, "elbonian money 2", notary = notary, contractState = 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner)
if (!withError) { if (!withError) {
command(issuer.party.owningKey) { Cash.Commands.Issue() } command(issuer.party.owningKey, Cash.Commands.Issue())
} else { } else {
// Put a broken command on so at least a signature is created // Put a broken command on so at least a signature is created
command(issuer.party.owningKey) { Cash.Commands.Move() } command(issuer.party.owningKey, Cash.Commands.Move())
} }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
if (withError) { if (withError) {
@ -672,16 +672,16 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
// Bob gets some cash onto the ledger from BoE // Bob gets some cash onto the ledger from BoE
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 1") input("elbonian money 1")
output(Cash.PROGRAM_ID, "bob cash 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy owner } output(Cash.PROGRAM_ID, "bob cash 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy owner)
command(interimOwner.owningKey) { Cash.Commands.Move() } command(interimOwner.owningKey, Cash.Commands.Move())
this.verifies() this.verifies()
} }
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 2") input("elbonian money 2")
output(Cash.PROGRAM_ID, "bob cash 2", notary = notary) { 300.DOLLARS.CASH issuedBy issuer ownedBy owner } output(Cash.PROGRAM_ID, "bob cash 2", notary = notary, contractState = 300.DOLLARS.CASH issuedBy issuer ownedBy owner)
output(Cash.PROGRAM_ID, notary = notary) { 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } // Change output. output(Cash.PROGRAM_ID, notary = notary, contractState = 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) // Change output.
command(interimOwner.owningKey) { Cash.Commands.Move() } command(interimOwner.owningKey, Cash.Commands.Move())
this.verifies() this.verifies()
} }
@ -697,10 +697,9 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
attachmentID: SecureHash?, attachmentID: SecureHash?,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
output(CommercialPaper.CP_PROGRAM_ID, "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) contractState = CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days))
} command(issuer.party.owningKey, CommercialPaper.Commands.Issue())
command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() }
if (!withError) if (!withError)
timeWindow(time = TEST_TX_TIME) timeWindow(time = TEST_TX_TIME)
if (attachmentID != null) if (attachmentID != null)

View File

@ -1,7 +1,7 @@
package net.corda.node.services package net.corda.node.services
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test

View File

@ -1,7 +1,7 @@
package net.corda.node.services.config package net.corda.node.services.config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import net.corda.nodeapi.config.toProperties import net.corda.nodeapi.internal.config.toProperties
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals

View File

@ -12,9 +12,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.StatesToRecord
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.node.internal.FlowStarterImpl import net.corda.node.internal.FlowStarterImpl
@ -44,7 +42,6 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.nio.file.Paths import java.nio.file.Paths
import java.security.PublicKey
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@ -105,13 +102,9 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
doReturn(MonitoringService(MetricRegistry())).whenever(it).monitoringService doReturn(MonitoringService(MetricRegistry())).whenever(it).monitoringService
doReturn(validatedTransactions).whenever(it).validatedTransactions doReturn(validatedTransactions).whenever(it).validatedTransactions
doReturn(NetworkMapCacheImpl(MockNetworkMapCache(database), identityService)).whenever(it).networkMapCache doReturn(NetworkMapCacheImpl(MockNetworkMapCache(database), identityService)).whenever(it).networkMapCache
doCallRealMethod().whenever(it).signInitialTransaction(any(), any<PublicKey>())
doReturn(myInfo).whenever(it).myInfo doReturn(myInfo).whenever(it).myInfo
doReturn(kms).whenever(it).keyManagementService doReturn(kms).whenever(it).keyManagementService
doReturn(CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.testing.contracts")), MockAttachmentStorage())).whenever(it).cordappProvider doReturn(CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.testing.contracts")), MockAttachmentStorage())).whenever(it).cordappProvider
doCallRealMethod().whenever(it).recordTransactions(any<StatesToRecord>(), any())
doCallRealMethod().whenever(it).recordTransactions(any<Iterable<SignedTransaction>>())
doCallRealMethod().whenever(it).recordTransactions(any<SignedTransaction>(), anyVararg())
doReturn(NodeVaultService(testClock, kms, stateLoader, database.hibernateConfig)).whenever(it).vaultService doReturn(NodeVaultService(testClock, kms, stateLoader, database.hibernateConfig)).whenever(it).vaultService
doReturn(this@NodeSchedulerServiceTest).whenever(it).testReference doReturn(this@NodeSchedulerServiceTest).whenever(it).testReference

View File

@ -52,7 +52,7 @@ class NetworkMapUpdaterTest {
val networkMapClient = mock<NetworkMapClient>() val networkMapClient = mock<NetworkMapClient>()
val scheduler = TestScheduler() val scheduler = TestScheduler()
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler) val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient) val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
// Publish node info for the first time. // Publish node info for the first time.
@ -101,7 +101,7 @@ class NetworkMapUpdaterTest {
} }
val scheduler = TestScheduler() val scheduler = TestScheduler()
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler) val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient) val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
// Test adding new node. // Test adding new node.
@ -155,7 +155,7 @@ class NetworkMapUpdaterTest {
} }
val scheduler = TestScheduler() val scheduler = TestScheduler()
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler) val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient) val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
// Add all nodes. // Add all nodes.
@ -199,7 +199,7 @@ class NetworkMapUpdaterTest {
val networkMapCache = getMockNetworkMapCache() val networkMapCache = getMockNetworkMapCache()
val scheduler = TestScheduler() val scheduler = TestScheduler()
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler) val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, null) val updater = NetworkMapUpdater(networkMapCache, fileWatcher, null)
// Not subscribed yet. // Not subscribed yet.

View File

@ -4,7 +4,7 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B
import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.PortAllocation

View File

@ -1,7 +1,7 @@
package net.corda.attachmentdemo package net.corda.attachmentdemo
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B
import net.corda.testing.driver.driver import net.corda.testing.driver.driver

View File

@ -10,7 +10,7 @@ import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test

View File

@ -12,7 +12,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.BOC import net.corda.testing.BOC
import net.corda.testing.internal.demorun.* import net.corda.testing.internal.demorun.*
import java.util.* import java.util.*

View File

@ -385,8 +385,8 @@ class IRSTests {
return ledger { return ledger {
transaction("Agreement") { transaction("Agreement") {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } output(IRS_PROGRAM_ID, "irs post agreement", singleIRS())
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -395,17 +395,14 @@ class IRSTests {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
input("irs post agreement") input("irs post agreement")
val postAgreement = "irs post agreement".output<InterestRateSwap.State>() val postAgreement = "irs post agreement".output<InterestRateSwap.State>()
output(IRS_PROGRAM_ID, "irs post first fixing") { output(IRS_PROGRAM_ID, "irs post first fixing",
postAgreement.copy( postAgreement.copy(
postAgreement.fixedLeg, postAgreement.fixedLeg,
postAgreement.floatingLeg, postAgreement.floatingLeg,
postAgreement.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))), postAgreement.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))),
postAgreement.common postAgreement.common))
) command(ORACLE_PUBKEY,
} InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
command(ORACLE_PUBKEY) {
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))
}
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -419,7 +416,7 @@ class IRSTests {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
input(IRS_PROGRAM_ID, irs) input(IRS_PROGRAM_ID, irs)
output(IRS_PROGRAM_ID, "irs post agreement", irs) output(IRS_PROGRAM_ID, "irs post agreement", irs)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are no in states for an agreement" this `fails with` "There are no in states for an agreement"
} }
@ -432,7 +429,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, 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() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are events in the fix schedule" this `fails with` "There are events in the fix schedule"
} }
@ -445,7 +442,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, 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() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "There are events in the float schedule" this `fails with` "There are events in the float schedule"
} }
@ -457,7 +454,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, 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() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All notionals must be non zero" this `fails with` "All notionals must be non zero"
} }
@ -465,7 +462,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, 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() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All notionals must be non zero" this `fails with` "All notionals must be non zero"
} }
@ -478,7 +475,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS) output(IRS_PROGRAM_ID, modifiedIRS)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The fixed leg rate must be positive" this `fails with` "The fixed leg rate must be positive"
} }
@ -494,7 +491,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS) output(IRS_PROGRAM_ID, modifiedIRS)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The currency of the notionals must be the same" this `fails with` "The currency of the notionals must be the same"
} }
@ -507,7 +504,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS) output(IRS_PROGRAM_ID, modifiedIRS)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "All leg notionals must be the same" this `fails with` "All leg notionals must be the same"
} }
@ -520,7 +517,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS1) output(IRS_PROGRAM_ID, modifiedIRS1)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the fixed leg" this `fails with` "The effective date is before the termination date for the fixed leg"
} }
@ -529,7 +526,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS2) output(IRS_PROGRAM_ID, modifiedIRS2)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective date is before the termination date for the floating leg" this `fails with` "The effective date is before the termination date for the floating leg"
} }
@ -543,7 +540,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS3) output(IRS_PROGRAM_ID, modifiedIRS3)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The termination dates are aligned" this `fails with` "The termination dates are aligned"
} }
@ -553,7 +550,7 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, modifiedIRS4) output(IRS_PROGRAM_ID, modifiedIRS4)
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this `fails with` "The effective dates are aligned" this `fails with` "The effective dates are aligned"
} }
@ -567,8 +564,8 @@ class IRSTests {
transaction { transaction {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } output(IRS_PROGRAM_ID, "irs post agreement", singleIRS())
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -585,9 +582,8 @@ class IRSTests {
// Templated tweak for reference. A corrent fixing applied should be ok // Templated tweak for reference. A corrent fixing applied should be ok
tweak { tweak {
command(ORACLE_PUBKEY) { command(ORACLE_PUBKEY,
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
}
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
output(IRS_PROGRAM_ID, newIRS) output(IRS_PROGRAM_ID, newIRS)
this.verifies() this.verifies()
@ -595,7 +591,7 @@ class IRSTests {
// This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new // This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new
tweak { tweak {
command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
output(IRS_PROGRAM_ID, oldIRS) output(IRS_PROGRAM_ID, oldIRS)
this `fails with` "There is at least one difference in the IRS floating leg payment schedules" this `fails with` "There is at least one difference in the IRS floating leg payment schedules"
@ -603,42 +599,36 @@ class IRSTests {
// This tests tries to sneak in a change to another fixing (which may or may not be the latest one) // This tests tries to sneak in a change to another fixing (which may or may not be the latest one)
tweak { tweak {
command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.toList()[1] val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.toList()[1]
val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey]
val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY")))
output(IRS_PROGRAM_ID,
output(IRS_PROGRAM_ID) {
newIRS.copy( newIRS.copy(
newIRS.fixedLeg, newIRS.fixedLeg,
newIRS.floatingLeg, newIRS.floatingLeg,
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
Pair(firstResetKey, modifiedFirstResetValue))), Pair(firstResetKey, modifiedFirstResetValue))),
newIRS.common newIRS.common))
)
}
this `fails with` "There is only one change in the IRS floating leg payment schedule" this `fails with` "There is only one change in the IRS floating leg payment schedule"
} }
// This tests modifies the payment currency for the fixing // This tests modifies the payment currency for the fixing
tweak { tweak {
command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } command(ORACLE_PUBKEY, InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key }
val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY")))
output(IRS_PROGRAM_ID,
output(IRS_PROGRAM_ID) {
newIRS.copy( newIRS.copy(
newIRS.fixedLeg, newIRS.fixedLeg,
newIRS.floatingLeg, newIRS.floatingLeg,
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus( newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
Pair(latestReset.key, modifiedLatestResetValue))), Pair(latestReset.key, modifiedLatestResetValue))),
newIRS.common newIRS.common))
)
}
this `fails with` "The fix payment has the same currency as the notional" this `fails with` "The fix payment has the same currency as the notional"
} }
} }
@ -660,31 +650,27 @@ class IRSTests {
return ledger { return ledger {
transaction("Agreement") { transaction("Agreement") {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, "irs post agreement1") { output(IRS_PROGRAM_ID, "irs post agreement1",
irs.copy( irs.copy(
irs.fixedLeg, irs.fixedLeg,
irs.floatingLeg, irs.floatingLeg,
irs.calculation, irs.calculation,
irs.common.copy(tradeID = "t1") irs.common.copy(tradeID = "t1")))
) command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
transaction("Agreement") { transaction("Agreement") {
attachments(IRS_PROGRAM_ID) attachments(IRS_PROGRAM_ID)
output(IRS_PROGRAM_ID, "irs post agreement2") { output(IRS_PROGRAM_ID, "irs post agreement2",
irs.copy( irs.copy(
linearId = UniqueIdentifier("t2"), linearId = UniqueIdentifier("t2"),
fixedLeg = irs.fixedLeg, fixedLeg = irs.fixedLeg,
floatingLeg = irs.floatingLeg, floatingLeg = irs.floatingLeg,
calculation = irs.calculation, calculation = irs.calculation,
common = irs.common.copy(tradeID = "t2") common = irs.common.copy(tradeID = "t2")))
) command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
}
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }
@ -694,27 +680,21 @@ class IRSTests {
input("irs post agreement1") input("irs post agreement1")
input("irs post agreement2") input("irs post agreement2")
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>() val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>()
output(IRS_PROGRAM_ID, "irs post first fixing1") { output(IRS_PROGRAM_ID, "irs post first fixing1",
postAgreement1.copy( postAgreement1.copy(
postAgreement1.fixedLeg, postAgreement1.fixedLeg,
postAgreement1.floatingLeg, postAgreement1.floatingLeg,
postAgreement1.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))), postAgreement1.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))),
postAgreement1.common.copy(tradeID = "t1") postAgreement1.common.copy(tradeID = "t1")))
)
}
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>() val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>()
output(IRS_PROGRAM_ID, "irs post first fixing2") { output(IRS_PROGRAM_ID, "irs post first fixing2",
postAgreement2.copy( postAgreement2.copy(
postAgreement2.fixedLeg, postAgreement2.fixedLeg,
postAgreement2.floatingLeg, postAgreement2.floatingLeg,
postAgreement2.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))), postAgreement2.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))),
postAgreement2.common.copy(tradeID = "t2") postAgreement2.common.copy(tradeID = "t2")))
) command(ORACLE_PUBKEY,
} InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1)))
command(ORACLE_PUBKEY) {
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1))
}
timeWindow(TEST_TX_TIME) timeWindow(TEST_TX_TIME)
this.verifies() this.verifies()
} }

View File

@ -21,7 +21,7 @@ import net.corda.finance.plugin.registerFinanceJSONMappers
import net.corda.irs.contract.InterestRateSwap import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.web.IrsDemoWebApplication import net.corda.irs.web.IrsDemoWebApplication
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.test.spring.springDriver import net.corda.test.spring.springDriver
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.http.HttpApi import net.corda.testing.http.HttpApi

View File

@ -4,7 +4,7 @@ import net.corda.cordform.CordformContext
import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformDefinition
import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY

View File

@ -8,7 +8,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.BOC import net.corda.testing.BOC
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B

View File

@ -4,7 +4,7 @@ import net.corda.core.internal.div
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.node.services.Permissions.Companion.all import net.corda.node.services.Permissions.Companion.all
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.BOC import net.corda.testing.BOC
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B

View File

@ -9,7 +9,7 @@ import net.corda.core.internal.read
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.Ignore import org.junit.Ignore

View File

@ -1,52 +0,0 @@
@file:JvmName("DriverConstants")
package net.corda.testing
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.CordaRPCOps
import net.corda.nodeapi.User
import net.corda.testing.driver.DriverDSLExposedInterface
//
// Extensions to the Driver DSL to auto-manufacture nodes by name.
//
/**
* A simple wrapper for objects provided by the integration test driver DSL. The fields are lazy so
* node construction won't start until you access the members. You can get one of these from the
* [alice], [bob] and [aliceAndBob] functions.
*/
class PredefinedTestNode internal constructor(party: Party, driver: DriverDSLExposedInterface) {
val rpcUsers = listOf(User("admin", "admin", setOf("ALL"))) // TODO: Randomize?
val nodeFuture by lazy { driver.startNode(providedName = party.name, rpcUsers = rpcUsers) }
val node by lazy { nodeFuture.get()!! }
val rpc by lazy { node.rpcClientToNode() }
fun <R> useRPC(block: (CordaRPCOps) -> R) = rpc.use(rpcUsers[0].username, rpcUsers[0].password) { block(it.proxy) }
}
// TODO: Probably we should inject the above keys through the driver to make the nodes use it, rather than have the warnings below.
/**
* Returns a plain, entirely stock node pre-configured with the [ALICE] identity. Note that a random key will be generated
* for it: you won't have [ALICE_KEY].
*/
fun DriverDSLExposedInterface.alice(): PredefinedTestNode = PredefinedTestNode(ALICE, this)
/**
* Returns a plain, entirely stock node pre-configured with the [BOB] identity. Note that a random key will be generated
* for it: you won't have [BOB_KEY].
*/
fun DriverDSLExposedInterface.bob(): PredefinedTestNode = PredefinedTestNode(BOB, this)
/**
* Returns plain, entirely stock nodes pre-configured with the [ALICE] and [BOB] X.500 names in that order. They have been
* started up in parallel and are now ready to use.
*/
fun DriverDSLExposedInterface.aliceAndBob(): List<PredefinedTestNode> {
val alice = alice()
val bob = bob()
listOf(alice.nodeFuture, bob.nodeFuture).transpose().get()
return listOf(alice, bob)
}

View File

@ -2,7 +2,6 @@
package net.corda.testing package net.corda.testing
import com.nhaarman.mockito_kotlin.doCallRealMethod
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.context.Actor import net.corda.core.context.Actor
@ -21,7 +20,7 @@ import net.corda.node.services.config.CertChainPolicyConfig
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.VerifierType import net.corda.node.services.config.VerifierType
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import java.nio.file.Path import java.nio.file.Path
@ -74,11 +73,6 @@ fun testNodeConfiguration(
doReturn(5).whenever(it).messageRedeliveryDelaySeconds doReturn(5).whenever(it).messageRedeliveryDelaySeconds
doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec
doReturn(null).whenever(it).devModeOptions doReturn(null).whenever(it).devModeOptions
doCallRealMethod().whenever(it).certificatesDirectory
doCallRealMethod().whenever(it).trustStoreFile
doCallRealMethod().whenever(it).sslKeystore
doCallRealMethod().whenever(it).nodeKeystore
doCallRealMethod().whenever(it).rootCaCertFile
} }
} }

View File

@ -26,7 +26,6 @@ import net.corda.core.utilities.*
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.Permissions.Companion.invokeRpc import net.corda.node.services.Permissions.Companion.invokeRpc
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.BFTNonValidatingNotaryService
@ -36,9 +35,9 @@ import net.corda.nodeapi.internal.ServiceIdentityGenerator
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
import net.corda.node.utilities.registration.NetworkRegistrationHelper import net.corda.node.utilities.registration.NetworkRegistrationHelper
import net.corda.nodeapi.internal.NodeInfoFilesCopier import net.corda.nodeapi.internal.NodeInfoFilesCopier
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.nodeapi.internal.NotaryInfo import net.corda.nodeapi.internal.NotaryInfo
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
@ -46,6 +45,7 @@ import net.corda.testing.*
import net.corda.nodeapi.internal.NetworkParametersCopier import net.corda.nodeapi.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.DriverDSL.ClusterType.* import net.corda.testing.driver.DriverDSL.ClusterType.*
import net.corda.testing.internal.InProcessNode
import net.corda.testing.internal.ProcessUtilities import net.corda.testing.internal.ProcessUtilities
import net.corda.testing.node.ClusterSpec import net.corda.testing.node.ClusterSpec
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
@ -1120,12 +1120,7 @@ class DriverDSL(
// Write node.conf // Write node.conf
writeConfig(nodeConf.baseDirectory, "node.conf", config) writeConfig(nodeConf.baseDirectory, "node.conf", config)
// TODO pass the version in? // TODO pass the version in?
val node = Node( val node = InProcessNode(nodeConf, MOCK_VERSION_INFO, cordappPackages).start()
nodeConf,
MOCK_VERSION_INFO,
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages))
.start()
val nodeThread = thread(name = nodeConf.myLegalName.organisation) { val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
node.internals.run() node.internals.run()
} }

View File

@ -7,25 +7,28 @@ import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.node.VersionInfo
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.*
import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.configOf import net.corda.node.services.config.configOf
import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.node.services.config.plus import net.corda.node.services.config.plus
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.SerializationEnvironmentRule
import net.corda.nodeapi.internal.NetworkParametersCopier import net.corda.nodeapi.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.driver.addressMustNotBeBoundFuture import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts import net.corda.testing.getFreeLocalPorts
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import org.apache.logging.log4j.Level import org.apache.logging.log4j.Level
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import rx.internal.schedulers.CachedThreadScheduler
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -100,11 +103,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
val parsedConfig = config.parseAsNodeConfiguration() val parsedConfig = config.parseAsNodeConfiguration()
defaultNetworkParameters.install(baseDirectory) defaultNetworkParameters.install(baseDirectory)
val node = Node( val node = InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion), cordappPackages).start()
parsedConfig,
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
nodes += node nodes += node
ensureAllNetworkMapCachesHaveAllNodeInfos() ensureAllNetworkMapCachesHaveAllNodeInfos()
thread(name = legalName.organisation) { thread(name = legalName.organisation) {
@ -127,3 +126,9 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
} }
} }
} }
class InProcessNode(
configuration: NodeConfiguration, versionInfo: VersionInfo, cordappPackages: List<String>) : Node(
configuration, versionInfo, false, CordappLoader.createDefaultWithTestPackages(configuration, cordappPackages)) {
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
}

View File

@ -23,7 +23,7 @@ import net.corda.node.services.messaging.RPCServerConfiguration
import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
import net.corda.testing.driver.* import net.corda.testing.driver.*
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec

View File

@ -6,8 +6,8 @@ import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformNode import net.corda.cordform.CordformNode
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.NotaryConfig
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.config.toConfig
fun CordformDefinition.node(configure: CordformNode.() -> Unit) { fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
addNode { cordformNode -> cordformNode.configure() } addNode { cordformNode -> cordformNode.configure() }

View File

@ -42,12 +42,14 @@ import net.corda.nodeapi.internal.NotaryInfo
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.nodeapi.internal.NetworkParametersCopier import net.corda.nodeapi.internal.NetworkParametersCopier
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.testThreadFactory
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import net.corda.testing.setGlobalSerialization import net.corda.testing.setGlobalSerialization
import net.corda.testing.testNodeConfiguration import net.corda.testing.testNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.apache.sshd.common.util.security.SecurityUtils import org.apache.sshd.common.util.security.SecurityUtils
import rx.internal.schedulers.CachedThreadScheduler
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
@ -267,6 +269,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
return started return started
} }
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
private fun advertiseNodeToNetwork(newNode: StartedNode<MockNode>) { private fun advertiseNodeToNetwork(newNode: StartedNode<MockNode>) {
mockNet.nodes mockNet.nodes
.mapNotNull { it.started } .mapNotNull { it.started }

View File

@ -2,7 +2,7 @@ package net.corda.testing.node
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.node.services.config.VerifierType import net.corda.node.services.config.VerifierType
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
data class NotarySpec( data class NotarySpec(
val name: CordaX500Name, val name: CordaX500Name,

View File

@ -6,7 +6,7 @@ import com.typesafe.config.ConfigRenderOptions
import com.typesafe.config.ConfigValue import com.typesafe.config.ConfigValue
import com.typesafe.config.ConfigValueFactory import com.typesafe.config.ConfigValueFactory
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
class NodeConfig( class NodeConfig(
val legalName: CordaX500Name, val legalName: CordaX500Name,

View File

@ -16,7 +16,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509CertificateFactory
@ -24,6 +24,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import org.mockito.internal.stubbing.answers.ThrowsException import org.mockito.internal.stubbing.answers.ThrowsException
import java.lang.reflect.Modifier
import java.nio.file.Files import java.nio.file.Files
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
@ -180,11 +181,16 @@ class UndefinedMockBehaviorException(message: String) : RuntimeException(message
inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java) inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java)
/** /**
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all methods. * Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all abstract methods,
* @param T the type to mock. Note if you want to use [com.nhaarman.mockito_kotlin.doCallRealMethod] on a Kotlin interface, * and [org.mockito.invocation.InvocationOnMock.callRealMethod] as the default for all concrete methods.
* @param T the type to mock. Note if you want concrete methods of a Kotlin interface to be invoked,
* it won't work unless you mock a (trivial) abstract implementation of that interface instead. * it won't work unless you mock a (trivial) abstract implementation of that interface instead.
*/ */
fun <T> rigorousMock(clazz: Class<T>): T = mock(clazz) { fun <T> rigorousMock(clazz: Class<T>): T = mock(clazz) {
// Use ThrowsException to hack the stack trace, and lazily so we can customise the message: if (Modifier.isAbstract(it.method.modifiers)) {
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it) // Use ThrowsException to hack the stack trace, and lazily so we can customise the message:
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it)
} else {
it.callRealMethod()
}
} }

View File

@ -93,7 +93,7 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder.addInputState(StateAndRef(state, stateRef)) transactionBuilder.addInputState(StateAndRef(state, stateRef))
} }
override fun _output(contractClassName: ContractClassName, override fun output(contractClassName: ContractClassName,
label: String?, label: String?,
notary: Party, notary: Party,
encumbrance: Int?, encumbrance: Int?,
@ -115,7 +115,7 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder.addAttachment(attachmentId) transactionBuilder.addAttachment(attachmentId)
} }
override fun _command(signers: List<PublicKey>, commandData: CommandData) { override fun command(signers: List<PublicKey>, commandData: CommandData) {
val command = Command(commandData, signers) val command = Command(commandData, signers)
transactionBuilder.addCommand(command) transactionBuilder.addCommand(command)
} }

View File

@ -35,12 +35,12 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
* @param contractState The state itself. * @param contractState The state itself.
* @param contractClassName The class name of the contract that verifies this state. * @param contractClassName The class name of the contract that verifies this state.
*/ */
fun _output(contractClassName: ContractClassName, fun output(contractClassName: ContractClassName,
label: String?, label: String?,
notary: Party, notary: Party,
encumbrance: Int?, encumbrance: Int?,
attachmentConstraint: AttachmentConstraint, attachmentConstraint: AttachmentConstraint,
contractState: ContractState) contractState: ContractState)
/** /**
* Adds an [Attachment] reference to the transaction. * Adds an [Attachment] reference to the transaction.
@ -53,7 +53,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
* @param signers The signer public keys. * @param signers The signer public keys.
* @param commandData The contents of the command. * @param commandData The contents of the command.
*/ */
fun _command(signers: List<PublicKey>, commandData: CommandData) fun command(signers: List<PublicKey>, commandData: CommandData)
/** /**
* Sets the time-window of the transaction. * Sets the time-window of the transaction.
@ -74,10 +74,10 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
fun _attachment(contractClassName: ContractClassName) fun _attachment(contractClassName: ContractClassName)
} }
class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : TransactionDSLInterpreter by interpreter { class TransactionDSL<out T : TransactionDSLInterpreter>(interpreter: T) : TransactionDSLInterpreter by interpreter {
/** /**
* Looks up the output label and adds the found state as an input. * Looks up the output label and adds the found state as an input.
* @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter._output] and friends. * @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter.output] and friends.
*/ */
fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref) fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref)
@ -88,49 +88,51 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
*/ */
fun input(contractClassName: ContractClassName, state: ContractState) { fun input(contractClassName: ContractClassName, state: ContractState) {
val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) { val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) {
output(contractClassName, attachmentConstraint = AlwaysAcceptAttachmentConstraint) { state } output(contractClassName, null, DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, state)
} }
input(transaction.outRef<ContractState>(0).ref) input(transaction.outRef<ContractState>(0).ref)
} }
fun input(contractClassName: ContractClassName, stateClosure: () -> ContractState) = input(contractClassName, stateClosure())
/** /**
* Adds an output to the transaction. * Adds a labelled output to the transaction.
*/ */
@JvmOverloads fun output(contractClassName: ContractClassName, label: String, notary: Party, contractState: ContractState) =
fun output(contractClassName: ContractClassName, output(contractClassName, label, notary, null, AutomaticHashConstraint, contractState)
label: String? = null,
notary: Party = DUMMY_NOTARY,
encumbrance: Int? = null,
attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint,
contractStateClosure: () -> ContractState) =
_output(contractClassName, label, notary, encumbrance, attachmentConstraint, contractStateClosure())
/** /**
* Adds a labelled output to the transaction. * Adds a labelled output to the transaction.
*/ */
@JvmOverloads fun output(contractClassName: ContractClassName, label: String, encumbrance: Int, contractState: ContractState) =
fun output(contractClassName: ContractClassName, label: String, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) = output(contractClassName, label, DUMMY_NOTARY, encumbrance, AutomaticHashConstraint, contractState)
_output(contractClassName, label, DUMMY_NOTARY, null, attachmentConstraint, contractState)
/**
* Adds a labelled output to the transaction.
*/
fun output(contractClassName: ContractClassName, label: String, contractState: ContractState) =
output(contractClassName, label, DUMMY_NOTARY, null, AutomaticHashConstraint, contractState)
/** /**
* Adds an output to the transaction. * Adds an output to the transaction.
*/ */
@JvmOverloads fun output(contractClassName: ContractClassName, notary: Party, contractState: ContractState) =
fun output(contractClassName: ContractClassName, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) = output(contractClassName, null, notary, null, AutomaticHashConstraint, contractState)
_output(contractClassName, null, DUMMY_NOTARY, null, attachmentConstraint, contractState)
/**
* Adds an output to the transaction.
*/
fun output(contractClassName: ContractClassName, encumbrance: Int, contractState: ContractState) =
output(contractClassName, null, DUMMY_NOTARY, encumbrance, AutomaticHashConstraint, contractState)
/**
* Adds an output to the transaction.
*/
fun output(contractClassName: ContractClassName, contractState: ContractState) =
output(contractClassName, null, DUMMY_NOTARY, null, AutomaticHashConstraint, contractState)
/** /**
* Adds a command to the transaction. * Adds a command to the transaction.
*/ */
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) = fun command(signer: PublicKey, commandData: CommandData) = command(listOf(signer), commandData)
_command(listOf(*signers), commandDataClosure())
/**
* Adds a command to the transaction.
*/
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData)
/** /**
* Sets the [TimeWindow] of the transaction. * Sets the [TimeWindow] of the transaction.
@ -142,7 +144,7 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
timeWindow(TimeWindow.withTolerance(time, tolerance)) timeWindow(TimeWindow.withTolerance(time, tolerance))
/** /**
* @see TransactionDSLInterpreter._contractAttachment * @see TransactionDSLInterpreter._attachment
*/ */
fun attachment(contractClassName: ContractClassName) = _attachment(contractClassName) fun attachment(contractClassName: ContractClassName) = _attachment(contractClassName)

View File

@ -5,7 +5,7 @@ import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ArtemisTcpTransport
import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.testing.configureTestSSL import net.corda.testing.configureTestSSL
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*

View File

@ -3,7 +3,7 @@ package net.corda.demobench.model
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.internal.config.parseAs
import tornadofx.* import tornadofx.*
import java.io.IOException import java.io.IOException
import java.nio.file.Files import java.nio.file.Files

View File

@ -6,8 +6,8 @@ import net.corda.core.internal.copyToDirectory
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption

View File

@ -5,8 +5,8 @@ import com.typesafe.config.ConfigValueFactory
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.webserver.WebServerConfig import net.corda.webserver.WebServerConfig
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test

View File

@ -2,7 +2,7 @@ package net.corda.demobench.model
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import org.junit.Test import org.junit.Test
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths

View File

@ -21,7 +21,7 @@ import net.corda.finance.flows.*
import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashExitFlow.ExitRequest
import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
import net.corda.node.services.Permissions.Companion.startFlow import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import net.corda.testing.ALICE import net.corda.testing.ALICE
import net.corda.testing.BOB import net.corda.testing.BOB
import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeHandle

View File

@ -1,6 +1,6 @@
package net.corda.loadtest package net.corda.loadtest
import net.corda.nodeapi.User import net.corda.nodeapi.internal.config.User
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinPool

Some files were not shown because too many files have changed in this diff Show More