mirror of
https://github.com/corda/corda.git
synced 2025-01-21 03:55:00 +00:00
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:
commit
6a1aa59e3e
137
CONTRIBUTORS.md
Normal file
137
CONTRIBUTORS.md
Normal 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)
|
@ -28,7 +28,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.driver
|
||||
import org.junit.Test
|
||||
|
@ -10,7 +10,7 @@ import net.corda.finance.flows.CashPaymentFlow;
|
||||
import net.corda.finance.schemas.CashSchemaV1;
|
||||
import net.corda.node.internal.Node;
|
||||
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.internal.NodeBasedTest;
|
||||
import org.junit.After;
|
||||
|
@ -20,7 +20,7 @@ import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.internal.NodeBasedTest
|
||||
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
|
||||
|
@ -15,7 +15,7 @@ import net.corda.core.utilities.*
|
||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
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.TransportConfiguration
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||
|
@ -9,7 +9,7 @@ import net.corda.core.messaging.FlowHandle;
|
||||
import net.corda.core.utilities.OpaqueBytes;
|
||||
import net.corda.finance.flows.AbstractCashFlow;
|
||||
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.NodeProcess;
|
||||
import org.junit.After;
|
||||
|
@ -21,7 +21,7 @@ import net.corda.finance.contracts.getCashBalance
|
||||
import net.corda.finance.contracts.getCashBalances
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
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.NodeProcess
|
||||
import org.apache.commons.io.output.NullOutputStream
|
||||
|
@ -5,7 +5,7 @@ import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.messaging.RPCOps
|
||||
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.internal.RPCDriverExposedDSLInterface
|
||||
import net.corda.testing.internal.rpcTestUser
|
||||
|
@ -4,7 +4,7 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.rpcDriver
|
||||
import org.junit.Test
|
||||
|
@ -7,7 +7,7 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.messaging.startFlow
|
||||
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.NodeProcess
|
||||
import net.corda.testing.common.internal.ProjectStructure
|
||||
|
@ -11,7 +11,7 @@ import net.corda.core.internal.list
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.NodeProcess
|
||||
import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME
|
||||
|
@ -38,24 +38,20 @@ class PartialMerkleTreeTest {
|
||||
testLedger = ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash") {
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash",
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
}
|
||||
output(Cash.PROGRAM_ID, "dummy cash 1") {
|
||||
owner = MEGA_CORP))
|
||||
output(Cash.PROGRAM_ID, "dummy cash 1",
|
||||
Cash.State(
|
||||
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MINI_CORP
|
||||
)
|
||||
}
|
||||
owner = MINI_CORP))
|
||||
}
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input("MEGA_CORP cash")
|
||||
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)
|
||||
this.verifies()
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.internal.SecureCordaRPCOps
|
||||
import net.corda.node.internal.StartedNode
|
||||
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.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyContractV2
|
||||
|
@ -20,6 +20,42 @@ class ReceiveMultipleFlowTests {
|
||||
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
|
||||
fun `receive all messages in parallel using map style`() {
|
||||
val doubleValue = 5.0
|
||||
|
@ -55,10 +55,10 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID) { state }
|
||||
output(Cash.PROGRAM_ID, encumbrance = 1) { stateWithNewOwner }
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, encumbrance = 1, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
@ -69,16 +69,16 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state }
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
}
|
||||
// Un-encumber the output if the time of the transaction is later than the timelock.
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input("state encumbered by 5pm time-lock")
|
||||
input("5pm time-lock")
|
||||
output(Cash.PROGRAM_ID) { stateWithNewOwner }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, stateWithNewOwner)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
timeWindow(FIVE_PM)
|
||||
verifies()
|
||||
}
|
||||
@ -90,16 +90,16 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock") { state }
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
}
|
||||
// The time of the transaction is earlier than the time specified in the encumbering timelock.
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input("state encumbered by 5pm time-lock")
|
||||
input("5pm time-lock")
|
||||
output(Cash.PROGRAM_ID) { state }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, state)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
timeWindow(FOUR_PM)
|
||||
this `fails with` "the time specified in the time-lock has passed"
|
||||
}
|
||||
@ -111,14 +111,14 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state }
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
}
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input("state encumbered by 5pm time-lock")
|
||||
output(Cash.PROGRAM_ID) { stateWithNewOwner }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, stateWithNewOwner)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
timeWindow(FIVE_PM)
|
||||
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||
}
|
||||
@ -130,9 +130,9 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { state }
|
||||
output(Cash.PROGRAM_ID, encumbrance = 0) { stateWithNewOwner }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(Cash.PROGRAM_ID, encumbrance = 0, contractState = stateWithNewOwner)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
this `fails with` "Missing required encumbrance 0 in OUTPUT"
|
||||
}
|
||||
}
|
||||
@ -143,10 +143,10 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input(Cash.PROGRAM_ID) { state }
|
||||
output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner }
|
||||
output(TEST_TIMELOCK_ID) { timeLock }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, state)
|
||||
output(TEST_TIMELOCK_ID, encumbrance = 2, contractState = stateWithNewOwner)
|
||||
output(TEST_TIMELOCK_ID, timeLock)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
this `fails with` "Missing required encumbrance 2 in OUTPUT"
|
||||
}
|
||||
}
|
||||
@ -157,16 +157,16 @@ class TransactionEncumbranceTests {
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state }
|
||||
output(Cash.PROGRAM_ID, "some other state") { state }
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock }
|
||||
output(Cash.PROGRAM_ID, "state encumbered by some other state", encumbrance = 1, contractState = state)
|
||||
output(Cash.PROGRAM_ID, "some other state", state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", timeLock)
|
||||
}
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
input("state encumbered by some other state")
|
||||
input("5pm time-lock")
|
||||
output(Cash.PROGRAM_ID) { stateWithNewOwner }
|
||||
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, stateWithNewOwner)
|
||||
command(MEGA_CORP.owningKey, Cash.Commands.Move())
|
||||
timeWindow(FIVE_PM)
|
||||
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||
}
|
||||
|
@ -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
|
||||
: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 corda start``
|
||||
* ``sudo systemctl corda-webserver start``
|
||||
* ``sudo systemctl enable --now corda``
|
||||
* ``sudo systemctl enable --now corda-webserver``
|
||||
|
||||
You can run multiple nodes by creating multiple directories and Corda services, modifying the ``node.conf`` and
|
||||
``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 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 AppStderr C:\Corda\service.log
|
||||
nssm set cordanode1 Description Corda Node - Bank of Breakfast Tea
|
||||
nssm set cordanode1 Start SERVICE_AUTO_START
|
||||
sc start cordanode1
|
||||
|
||||
9. Modify the batch file:
|
||||
|
@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.driver
|
||||
import org.junit.Test
|
||||
|
@ -17,7 +17,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.driver
|
||||
import org.graphstream.graph.Edge
|
||||
|
@ -24,6 +24,7 @@ object CustomVaultQuery {
|
||||
private companion object {
|
||||
private val log = contextLogger()
|
||||
}
|
||||
|
||||
fun rebalanceCurrencyReserves(): List<Amount<Currency>> {
|
||||
val nativeQuery = """
|
||||
select
|
||||
@ -44,16 +45,18 @@ object CustomVaultQuery {
|
||||
"""
|
||||
log.info("SQL to execute: $nativeQuery")
|
||||
val session = services.jdbcSession()
|
||||
val prepStatement = session.prepareStatement(nativeQuery)
|
||||
val rs = prepStatement.executeQuery()
|
||||
val topUpLimits: MutableList<Amount<Currency>> = mutableListOf()
|
||||
while (rs.next()) {
|
||||
val currencyStr = rs.getString(1)
|
||||
val amount = rs.getLong(2)
|
||||
log.info("$currencyStr : $amount")
|
||||
topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr)))
|
||||
return session.prepareStatement(nativeQuery).use { prepStatement ->
|
||||
prepStatement.executeQuery().use { rs ->
|
||||
val topUpLimits: MutableList<Amount<Currency>> = mutableListOf()
|
||||
while (rs.next()) {
|
||||
val currencyStr = rs.getString(1)
|
||||
val amount = rs.getLong(2)
|
||||
log.info("$currencyStr : $amount")
|
||||
topUpLimits.add(Amount(amount, Currency.getInstance(currencyStr)))
|
||||
}
|
||||
topUpLimits
|
||||
}
|
||||
}
|
||||
return topUpLimits
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,6 +72,7 @@ object TopupIssuerFlow {
|
||||
data class TopupRequest(val issueToParty: Party,
|
||||
val issuerPartyRef: OpaqueBytes,
|
||||
val notaryParty: Party)
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
class TopupIssuanceRequester(val issueToParty: Party,
|
||||
|
@ -33,7 +33,7 @@ class CommercialPaperTest {
|
||||
ledger {
|
||||
transaction {
|
||||
attachments(CP_PROGRAM_ID)
|
||||
input(CP_PROGRAM_ID) { inState }
|
||||
input(CP_PROGRAM_ID, inState)
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
@ -46,8 +46,8 @@ class CommercialPaperTest {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(CP_PROGRAM_ID) { inState }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
input(CP_PROGRAM_ID, inState)
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
verifies()
|
||||
}
|
||||
@ -61,8 +61,8 @@ class CommercialPaperTest {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(CP_PROGRAM_ID) { inState }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
input(CP_PROGRAM_ID, inState)
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
`fails with`("the state is propagated")
|
||||
}
|
||||
@ -76,11 +76,11 @@ class CommercialPaperTest {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(CP_PROGRAM_ID) { inState }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
input(CP_PROGRAM_ID, inState)
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
`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()
|
||||
}
|
||||
}
|
||||
@ -92,15 +92,15 @@ class CommercialPaperTest {
|
||||
fun `simple issuance with tweak`() {
|
||||
ledger {
|
||||
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)
|
||||
tweak {
|
||||
// The wrong pubkey.
|
||||
command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
`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)
|
||||
verifies()
|
||||
}
|
||||
@ -112,15 +112,15 @@ class CommercialPaperTest {
|
||||
@Test
|
||||
fun `simple issuance with tweak and top level 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)
|
||||
tweak {
|
||||
// The wrong pubkey.
|
||||
command(BIG_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
command(BIG_CORP_PUBKEY, CommercialPaper.Commands.Issue())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
`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)
|
||||
verifies()
|
||||
}
|
||||
@ -140,8 +140,8 @@ class CommercialPaperTest {
|
||||
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output(CP_PROGRAM_ID, "paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
output(CP_PROGRAM_ID, "paper", getPaper())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
timeWindow(TEST_TX_TIME)
|
||||
verifies()
|
||||
@ -151,10 +151,10 @@ class CommercialPaperTest {
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
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) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
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))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
@ -173,8 +173,8 @@ class CommercialPaperTest {
|
||||
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output(CP_PROGRAM_ID, "paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
output(CP_PROGRAM_ID, "paper", getPaper())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
timeWindow(TEST_TX_TIME)
|
||||
verifies()
|
||||
@ -183,18 +183,18 @@ class CommercialPaperTest {
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
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) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
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))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("paper")
|
||||
// We moved a paper to another pubkey.
|
||||
output(CP_PROGRAM_ID, "bob's paper") { "paper".output<ICommercialPaperState>().withOwner(BOB) }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
output(CP_PROGRAM_ID, "bob's paper", "paper".output<ICommercialPaperState>().withOwner(BOB))
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
|
||||
@ -215,8 +215,8 @@ class CommercialPaperTest {
|
||||
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output(CP_PROGRAM_ID, "paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
output(CP_PROGRAM_ID, "paper", getPaper())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Issue())
|
||||
attachments(CP_PROGRAM_ID)
|
||||
timeWindow(TEST_TX_TIME)
|
||||
verifies()
|
||||
@ -225,10 +225,10 @@ class CommercialPaperTest {
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
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) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
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))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
|
||||
@ -236,8 +236,8 @@ class CommercialPaperTest {
|
||||
transaction {
|
||||
input("paper")
|
||||
// We moved a paper to another pubkey.
|
||||
output(CP_PROGRAM_ID, "bob's paper") { "paper".output<ICommercialPaperState>().withOwner(BOB) }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
output(CP_PROGRAM_ID, "bob's paper", "paper".output<ICommercialPaperState>().withOwner(BOB))
|
||||
command(MEGA_CORP_PUBKEY, CommercialPaper.Commands.Move())
|
||||
verifies()
|
||||
}
|
||||
fails()
|
||||
|
@ -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
|
||||
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
|
||||
---------------------------------
|
||||
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
|
||||
|
||||
.. warning:: By default, the node will look for a configuration file called ``node.conf`` and a CorDapps folder called
|
||||
``cordapps`` in the current working directory. You can override the configuration file and workspace paths on the
|
||||
command line (e.g. ``./corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``).
|
||||
By default, the node will look for a configuration file called ``node.conf`` and a CorDapps folder called ``cordapps``
|
||||
in the current working directory. You can override the configuration file and workspace paths on the command line (e.g.
|
||||
``./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:
|
||||
|
||||
|
@ -172,16 +172,14 @@ class Cap {
|
||||
@Test
|
||||
fun issue() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateInitial }
|
||||
output(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -189,44 +187,38 @@ class Cap {
|
||||
@Test
|
||||
fun `first fixing`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateInitial }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -234,19 +226,16 @@ class Cap {
|
||||
@Test
|
||||
fun `first execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
|
||||
output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
|
||||
output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -254,18 +243,15 @@ class Cap {
|
||||
@Test
|
||||
fun `final execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal }
|
||||
output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal)
|
||||
output(UNIVERSAL_PROGRAM_ID, statePaymentFinal)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -273,44 +259,38 @@ class Cap {
|
||||
@Test
|
||||
fun `second fixing`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFinal)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -55,16 +55,14 @@ class Caplet {
|
||||
@Test
|
||||
fun issue() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateStart }
|
||||
output(UNIVERSAL_PROGRAM_ID, stateStart)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -72,17 +70,15 @@ class Caplet {
|
||||
@Test
|
||||
fun `execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateFixed }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateFinal }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateFixed)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateFinal)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -90,44 +86,38 @@ class Caplet {
|
||||
@Test
|
||||
fun `fixing`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateStart }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateFixed }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateStart)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateFixed)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -52,20 +52,18 @@ class FXFwdTimeOption {
|
||||
@Test
|
||||
fun `issue - signature`() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID, inState)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
}
|
||||
tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
|
||||
command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -73,31 +71,28 @@ class FXFwdTimeOption {
|
||||
@Test
|
||||
fun `maturity, bank exercise`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_AFTER_MATURITY)
|
||||
|
||||
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"
|
||||
}
|
||||
tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -105,31 +100,28 @@ class FXFwdTimeOption {
|
||||
@Test
|
||||
fun `maturity, corp exercise`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_BEFORE_MATURITY)
|
||||
|
||||
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"
|
||||
}
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("expire"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") }
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("expire"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") }
|
||||
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("exercise"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -44,20 +44,18 @@ class FXSwap {
|
||||
fun `issue - signature`() {
|
||||
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID, inState)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
}
|
||||
tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Issue())
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
|
||||
command(listOf(highStreetBank.owningKey, acmeCorp.owningKey), UniversalContract.Commands.Issue())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -65,18 +63,16 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -84,18 +80,16 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - reversed order`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -103,12 +97,11 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - not authorized`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
command(momAndPop.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
}
|
||||
@ -116,12 +109,11 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - before maturity`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_TOO_EARLY)
|
||||
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
}
|
||||
@ -129,11 +121,10 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - outState mismatch 1`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
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"
|
||||
}
|
||||
}
|
||||
@ -141,12 +132,11 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - outState mismatch 2`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateBad2 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateBad2)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this `fails with` "output states must match action result state"
|
||||
}
|
||||
}
|
||||
@ -154,12 +144,11 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - outState mismatch 3`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateBad1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState2 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateBad1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState2)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this `fails with` "output states must match action result state"
|
||||
}
|
||||
}
|
||||
@ -167,12 +156,11 @@ class FXSwap {
|
||||
@Test
|
||||
fun `execute - outState mismatch 4`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState1 }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateBad3 }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState1)
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateBad3)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this `fails with` "output states must match action result state"
|
||||
}
|
||||
}
|
||||
|
@ -134,16 +134,14 @@ class IRS {
|
||||
@Test
|
||||
fun issue() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateInitial }
|
||||
output(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -151,44 +149,38 @@ class IRS {
|
||||
@Test
|
||||
fun `first fixing`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateInitial }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
tweak {
|
||||
// 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"
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -196,19 +188,16 @@ class IRS {
|
||||
@Test
|
||||
fun `first execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst }
|
||||
output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, stateAfterFixingFirst)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateAfterExecutionFirst)
|
||||
output(UNIVERSAL_PROGRAM_ID, statePaymentFirst)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("pay floating") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("pay floating"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -145,16 +145,14 @@ class RollOutTests {
|
||||
@Test
|
||||
fun issue() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateStart }
|
||||
output(UNIVERSAL_PROGRAM_ID, stateStart)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -162,18 +160,16 @@ class RollOutTests {
|
||||
@Test
|
||||
fun `execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { stateStart }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateStep1a }
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateStep1b }
|
||||
input(UNIVERSAL_PROGRAM_ID, stateStart)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateStep1a)
|
||||
output(UNIVERSAL_PROGRAM_ID, stateStep1b)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
/* tweak {
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
|
||||
this `fails with` "action must be defined"
|
||||
}*/
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("transfer") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("transfer"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -61,16 +61,14 @@ class Swaption {
|
||||
@Test
|
||||
fun issue() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { stateInitial }
|
||||
output(UNIVERSAL_PROGRAM_ID, stateInitial)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -51,15 +51,12 @@ class ZeroCouponBond {
|
||||
@Test
|
||||
fun `issue - signature`() {
|
||||
transaction {
|
||||
output(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
|
||||
output(UNIVERSAL_PROGRAM_ID, inState)
|
||||
tweak {
|
||||
command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
|
||||
command(acmeCorp.owningKey, UniversalContract.Commands.Issue())
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -67,17 +64,15 @@ class ZeroCouponBond {
|
||||
@Test
|
||||
fun `execute`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState)
|
||||
timeWindow(TEST_TX_TIME_1)
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
|
||||
command(highStreetBank.owningKey, UniversalContract.Commands.Action("execute"))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -85,11 +80,10 @@ class ZeroCouponBond {
|
||||
@Test
|
||||
fun `execute - not authorized`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outState }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outState)
|
||||
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"
|
||||
}
|
||||
}
|
||||
@ -97,11 +91,10 @@ class ZeroCouponBond {
|
||||
@Test
|
||||
fun `execute - outState mismatch`() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateWrong }
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateWrong)
|
||||
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"
|
||||
}
|
||||
}
|
||||
@ -109,29 +102,23 @@ class ZeroCouponBond {
|
||||
@Test
|
||||
fun move() {
|
||||
transaction {
|
||||
input(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
|
||||
input(UNIVERSAL_PROGRAM_ID, inState)
|
||||
tweak {
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateMove }
|
||||
command(acmeCorp.owningKey) {
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop)
|
||||
}
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateMove)
|
||||
command(acmeCorp.owningKey,
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop))
|
||||
this `fails with` "the transaction is signed by all liable parties"
|
||||
}
|
||||
|
||||
tweak {
|
||||
output(UNIVERSAL_PROGRAM_ID) { inState }
|
||||
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop)
|
||||
}
|
||||
output(UNIVERSAL_PROGRAM_ID, inState)
|
||||
command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey),
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop))
|
||||
this `fails with` "output state does not reflect move command"
|
||||
}
|
||||
|
||||
output(UNIVERSAL_PROGRAM_ID) { outStateMove }
|
||||
|
||||
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop)
|
||||
}
|
||||
output(UNIVERSAL_PROGRAM_ID, outStateMove)
|
||||
command(listOf(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey),
|
||||
UniversalContract.Commands.Move(acmeCorp, momAndPop))
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -69,13 +69,14 @@ abstract class AbstractCashSelection {
|
||||
* with this notary are included.
|
||||
* @param onlyFromIssuerParties Optional issuer parties 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.
|
||||
* @return The result of the withResultSet function
|
||||
*/
|
||||
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.
|
||||
@ -124,34 +125,40 @@ abstract class AbstractCashSelection {
|
||||
try {
|
||||
// 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
|
||||
val rs = executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs)
|
||||
stateAndRefs.clear()
|
||||
return executeQuery(connection, amount, lockId, notary, onlyFromIssuerParties, withIssuerRefs) { rs ->
|
||||
stateAndRefs.clear()
|
||||
|
||||
var totalPennies = 0L
|
||||
val stateRefs = mutableSetOf<StateRef>()
|
||||
while (rs.next()) {
|
||||
val txHash = SecureHash.parse(rs.getString(1))
|
||||
val index = rs.getInt(2)
|
||||
val pennies = rs.getLong(3)
|
||||
totalPennies = rs.getLong(4)
|
||||
val rowLockId = rs.getString(5)
|
||||
stateRefs.add(StateRef(txHash, index))
|
||||
log.trace { "ROW: $rowLockId ($lockId): ${StateRef(txHash, index)} : $pennies ($totalPennies)" }
|
||||
var totalPennies = 0L
|
||||
val stateRefs = mutableSetOf<StateRef>()
|
||||
while (rs.next()) {
|
||||
val txHash = SecureHash.parse(rs.getString(1))
|
||||
val index = rs.getInt(2)
|
||||
val pennies = rs.getLong(3)
|
||||
totalPennies = rs.getLong(4)
|
||||
val rowLockId = rs.getString(5)
|
||||
stateRefs.add(StateRef(txHash, index))
|
||||
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
|
||||
} catch (e: SQLException) {
|
||||
log.error("""Failed retrieving unconsumed states for: amount [$amount], onlyFromIssuerParties [$onlyFromIssuerParties], notary [$notary], lockId [$lockId]
|
||||
|
@ -30,9 +30,8 @@ class CashSelectionH2Impl : AbstractCashSelection() {
|
||||
// 2) H2 uses session variables to perform this accumulator function:
|
||||
// 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)
|
||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
||||
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet {
|
||||
connection.createStatement().execute("CALL SET(@t, CAST(0 AS BIGINT));")
|
||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean {
|
||||
connection.createStatement().use { it.execute("CALL SET(@t, CAST(0 AS BIGINT));") }
|
||||
|
||||
val selectJoin = """
|
||||
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 "")
|
||||
|
||||
// Use prepared statement for protection against SQL Injection (http://www.h2database.com/html/advanced.html#sql_injection)
|
||||
val psSelectJoin = connection.prepareStatement(selectJoin)
|
||||
var pIndex = 0
|
||||
psSelectJoin.setString(++pIndex, amount.token.currencyCode)
|
||||
psSelectJoin.setLong(++pIndex, amount.quantity)
|
||||
psSelectJoin.setString(++pIndex, lockId.toString())
|
||||
if (notary != null)
|
||||
psSelectJoin.setString(++pIndex, notary.name.toString())
|
||||
if (onlyFromIssuerParties.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any}.toTypedArray() )
|
||||
if (withIssuerRefs.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray())
|
||||
log.debug { psSelectJoin.toString() }
|
||||
connection.prepareStatement(selectJoin).use { psSelectJoin ->
|
||||
var pIndex = 0
|
||||
psSelectJoin.setString(++pIndex, amount.token.currencyCode)
|
||||
psSelectJoin.setLong(++pIndex, amount.quantity)
|
||||
psSelectJoin.setString(++pIndex, lockId.toString())
|
||||
if (notary != null)
|
||||
psSelectJoin.setString(++pIndex, notary.name.toString())
|
||||
if (onlyFromIssuerParties.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any }.toTypedArray())
|
||||
if (withIssuerRefs.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray())
|
||||
log.debug { psSelectJoin.toString() }
|
||||
|
||||
return psSelectJoin.executeQuery()
|
||||
psSelectJoin.executeQuery().use { rs ->
|
||||
return withResultSet(rs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ class CashSelectionMySQLImpl : AbstractCashSelection() {
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
// 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
|
||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
||||
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet {
|
||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?, onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>, withResultSet: (ResultSet) -> Boolean): Boolean {
|
||||
val selectJoin = """SELECT nested.transaction_id, nested.output_index, nested.pennies,
|
||||
nested.total+nested.pennies as total_pennies, nested.lock_id
|
||||
FROM
|
||||
@ -51,29 +50,32 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() {
|
||||
nested WHERE nested.total < ?
|
||||
"""
|
||||
|
||||
val statement = connection.prepareStatement(selectJoin)
|
||||
statement.setString(1, amount.token.toString())
|
||||
statement.setString(2, lockId.toString())
|
||||
var paramOffset = 0
|
||||
if (notary != null) {
|
||||
statement.setString(3, notary.name.toString())
|
||||
paramOffset += 1
|
||||
}
|
||||
if (onlyFromIssuerParties.isNotEmpty()) {
|
||||
val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map
|
||||
{ it.owningKey.toBase58String() }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerKeys)
|
||||
paramOffset += 1
|
||||
}
|
||||
if (withIssuerRefs.isNotEmpty()) {
|
||||
val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map
|
||||
{ it.bytes }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerRefs)
|
||||
paramOffset += 1
|
||||
}
|
||||
statement.setLong(3 + paramOffset, amount.quantity)
|
||||
log.debug { statement.toString() }
|
||||
connection.prepareStatement(selectJoin).use { statement ->
|
||||
statement.setString(1, amount.token.toString())
|
||||
statement.setString(2, lockId.toString())
|
||||
var paramOffset = 0
|
||||
if (notary != null) {
|
||||
statement.setString(3, notary.name.toString())
|
||||
paramOffset += 1
|
||||
}
|
||||
if (onlyFromIssuerParties.isNotEmpty()) {
|
||||
val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map
|
||||
{ it.owningKey.toBase58String() }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerKeys)
|
||||
paramOffset += 1
|
||||
}
|
||||
if (withIssuerRefs.isNotEmpty()) {
|
||||
val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map
|
||||
{ it.bytes }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerRefs)
|
||||
paramOffset += 1
|
||||
}
|
||||
statement.setLong(3 + paramOffset, amount.quantity)
|
||||
log.debug { statement.toString() }
|
||||
|
||||
return statement.executeQuery()
|
||||
statement.executeQuery().use { rs ->
|
||||
return withResultSet(rs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,34 +32,34 @@ public class CashTestsJava {
|
||||
tx.input(Cash.PROGRAM_ID, inState);
|
||||
|
||||
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());
|
||||
return tw.failsWith("the amounts balance");
|
||||
});
|
||||
|
||||
tx.tweak(tw -> {
|
||||
tw.output(Cash.PROGRAM_ID, () -> outState);
|
||||
tw.output(Cash.PROGRAM_ID, outState);
|
||||
tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE);
|
||||
// Invalid command
|
||||
return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command");
|
||||
});
|
||||
tx.tweak(tw -> {
|
||||
tw.output(Cash.PROGRAM_ID, () -> outState);
|
||||
tw.output(Cash.PROGRAM_ID, outState);
|
||||
tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
return tw.failsWith("the owning keys are a subset of the signing keys");
|
||||
});
|
||||
tx.tweak(tw -> {
|
||||
tw.output(Cash.PROGRAM_ID, () -> outState);
|
||||
tw.output(Cash.PROGRAM_ID, outState);
|
||||
// issuedBy() can't be directly imported because it conflicts with other identically named functions
|
||||
// with different overloads (for some reason).
|
||||
tw.output(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());
|
||||
return tw.failsWith("at least one cash input");
|
||||
});
|
||||
|
||||
// Simple reallocation works.
|
||||
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());
|
||||
return tw.verifies();
|
||||
});
|
||||
|
@ -106,8 +106,8 @@ class CommercialPaperTestsGeneric {
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
attachments(CP_PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
output(thisTest.getContract(), "paper") { thisTest.getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||
output(thisTest.getContract(), "paper", thisTest.getPaper())
|
||||
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -118,10 +118,10 @@ class CommercialPaperTestsGeneric {
|
||||
attachments(Cash.PROGRAM_ID, JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
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) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
|
||||
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))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
command(MEGA_CORP_PUBKEY, thisTest.getMoveCommand())
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
@ -133,13 +133,11 @@ class CommercialPaperTestsGeneric {
|
||||
input("some profits")
|
||||
|
||||
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
||||
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, "Alice's profit", aliceGetsBack.STATE ownedBy ALICE)
|
||||
output(Cash.PROGRAM_ID, "Change", (someProfits - aliceGetsBack).STATE ownedBy MEGA_CORP)
|
||||
}
|
||||
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
command(ALICE_PUBKEY) { thisTest.getRedeemCommand(DUMMY_NOTARY) }
|
||||
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
command(ALICE_PUBKEY, thisTest.getRedeemCommand(DUMMY_NOTARY))
|
||||
tweak {
|
||||
outputs(700.DOLLARS `issued by` issuer)
|
||||
timeWindow(TEST_TX_TIME + 8.days)
|
||||
@ -155,7 +153,7 @@ class CommercialPaperTestsGeneric {
|
||||
timeWindow(TEST_TX_TIME + 8.days)
|
||||
|
||||
tweak {
|
||||
output(thisTest.getContract()) { "paper".output<ICommercialPaperState>() }
|
||||
output(thisTest.getContract(), "paper".output<ICommercialPaperState>())
|
||||
this `fails with` "must be destroyed"
|
||||
}
|
||||
|
||||
@ -169,8 +167,8 @@ class CommercialPaperTestsGeneric {
|
||||
transaction {
|
||||
attachment(CP_PROGRAM_ID)
|
||||
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
output(thisTest.getContract()) { thisTest.getPaper() }
|
||||
command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||
output(thisTest.getContract(), thisTest.getPaper())
|
||||
command(MINI_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "output states are issued by a command signer"
|
||||
}
|
||||
@ -181,8 +179,8 @@ class CommercialPaperTestsGeneric {
|
||||
transaction {
|
||||
attachment(CP_PROGRAM_ID)
|
||||
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||
output(thisTest.getContract(), thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer))
|
||||
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "output values sum to more than the inputs"
|
||||
}
|
||||
@ -193,8 +191,8 @@ class CommercialPaperTestsGeneric {
|
||||
transaction {
|
||||
attachment(CP_PROGRAM_ID)
|
||||
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||
output(thisTest.getContract(), thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days))
|
||||
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "maturity date is not in the past"
|
||||
}
|
||||
@ -206,8 +204,8 @@ class CommercialPaperTestsGeneric {
|
||||
attachment(CP_PROGRAM_ID)
|
||||
attachment(JavaCommercialPaper.JCP_PROGRAM_ID)
|
||||
input(thisTest.getContract(), thisTest.getPaper())
|
||||
output(thisTest.getContract()) { thisTest.getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||
output(thisTest.getContract(), thisTest.getPaper())
|
||||
command(MEGA_CORP_PUBKEY, thisTest.getIssueCommand(DUMMY_NOTARY))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "output values sum to more than the inputs"
|
||||
}
|
||||
|
@ -111,34 +111,33 @@ class CashTests {
|
||||
fun trivial() {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
tweak {
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
tweak {
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(ALICE_PUBKEY) { DummyCommandData }
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(ALICE_PUBKEY, DummyCommandData)
|
||||
// Invalid command
|
||||
this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command"
|
||||
}
|
||||
tweak {
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(BOB_PUBKEY) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(BOB_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the owning keys are a subset of the signing keys"
|
||||
}
|
||||
tweak {
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "at least one cash input"
|
||||
}
|
||||
// Simple reallocation works.
|
||||
tweak {
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -149,10 +148,9 @@ class CashTests {
|
||||
// Check we can't "move" money into existence.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { DummyState() }
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
|
||||
input(Cash.PROGRAM_ID, DummyState())
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(MINI_CORP_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "there is at least one cash input for this group"
|
||||
}
|
||||
}
|
||||
@ -163,19 +161,17 @@ class CashTests {
|
||||
// institution is allowed to issue as much cash as they want.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Issue() }
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Issue())
|
||||
this `fails with` "output states are issued by a command signer"
|
||||
}
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID) {
|
||||
output(Cash.PROGRAM_ID,
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
|
||||
owner = AnonymousParty(ALICE_PUBKEY)
|
||||
)
|
||||
}
|
||||
command(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
owner = AnonymousParty(ALICE_PUBKEY)))
|
||||
command(MINI_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
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.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { issuerInState }
|
||||
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) }
|
||||
|
||||
input(Cash.PROGRAM_ID, issuerInState)
|
||||
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
|
||||
// Move fails: not allowed to summon money.
|
||||
tweak {
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
|
||||
// Issue works.
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -230,29 +225,29 @@ class CashTests {
|
||||
// Can't use an issue command to lower the amount.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) }
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount.splitEvenly(2).first()))
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
this `fails with` "output values sum to more than the inputs"
|
||||
}
|
||||
|
||||
// Can't have an issue command that doesn't actually issue money.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { inState }
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, inState)
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
this `fails with` "output values sum to more than the inputs"
|
||||
}
|
||||
|
||||
// Can't have any other commands if we have an issue command (because the issue command overrules them)
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { inState.copy(amount = inState.amount * 2) }
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, inState.copy(amount = inState.amount * 2))
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Issue())
|
||||
this `fails with` "there is only a single issue command"
|
||||
}
|
||||
this.verifies()
|
||||
@ -282,26 +277,26 @@ class CashTests {
|
||||
// Splitting value works.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
tweak {
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
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()
|
||||
}
|
||||
// Merging 4 inputs into 2 outputs works.
|
||||
tweak {
|
||||
val splits2 = inState.amount.splitEvenly(2)
|
||||
val splits4 = inState.amount.splitEvenly(4)
|
||||
for (i in 0..3) input(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..3) input(Cash.PROGRAM_ID, inState.copy(amount = splits4[i]))
|
||||
for (i in 0..1) output(Cash.PROGRAM_ID, inState.copy(amount = splits2[i]))
|
||||
this.verifies()
|
||||
}
|
||||
// Merging 2 inputs into 1 works.
|
||||
tweak {
|
||||
val splits2 = inState.amount.splitEvenly(2)
|
||||
for (i in 0..1) input(Cash.PROGRAM_ID) { inState.copy(amount = splits2[i]) }
|
||||
output(Cash.PROGRAM_ID) { inState }
|
||||
for (i in 0..1) input(Cash.PROGRAM_ID, inState.copy(amount = splits2[i]))
|
||||
output(Cash.PROGRAM_ID, inState)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -311,17 +306,17 @@ class CashTests {
|
||||
fun zeroSizedValues() {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
input(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "zero sized inputs"
|
||||
}
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "zero sized outputs"
|
||||
}
|
||||
}
|
||||
@ -331,58 +326,56 @@ class CashTests {
|
||||
// Can't change issuer.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { outState issuedBy MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, outState issuedBy MINI_CORP)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't change deposit reference when splitting.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
val splits2 = inState.amount.splitEvenly(2)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
for (i in 0..1) output(Cash.PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
for (i in 0..1) output(Cash.PROGRAM_ID, outState.copy(amount = splits2[i]).editDepositRef(i.toByte()))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't mix currencies.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) }
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer))
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = 200.POUNDS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID) {
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
input(Cash.PROGRAM_ID,
|
||||
inState.copy(
|
||||
amount = 150.POUNDS `issued by` defaultIssuer,
|
||||
owner = AnonymousParty(BOB_PUBKEY)
|
||||
)
|
||||
}
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
owner = AnonymousParty(BOB_PUBKEY)))
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't have superfluous input states from different issuers.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP }
|
||||
output(Cash.PROGRAM_ID) { outState }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
|
||||
output(Cash.PROGRAM_ID, outState)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't combine two different deposits at the same issuer.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID) { inState.editDepositRef(3) }
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
input(Cash.PROGRAM_ID, inState.editDepositRef(3))
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount * 2).editDepositRef(3))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "for reference [01]"
|
||||
}
|
||||
}
|
||||
@ -392,21 +385,20 @@ class CashTests {
|
||||
// Single input/output straightforward case.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { issuerInState }
|
||||
output(Cash.PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
|
||||
|
||||
input(Cash.PROGRAM_ID, issuerInState)
|
||||
output(Cash.PROGRAM_ID, issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) }
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer))
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -418,20 +410,15 @@ class CashTests {
|
||||
// Multi-issuer case.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { issuerInState }
|
||||
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(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
|
||||
|
||||
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
|
||||
input(Cash.PROGRAM_ID, issuerInState)
|
||||
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(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)))
|
||||
command(listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Cash.Commands.Move())
|
||||
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"
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -441,10 +428,10 @@ class CashTests {
|
||||
// Single input/output straightforward case.
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
output(Cash.PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
output(Cash.PROGRAM_ID, outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)))
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
}
|
||||
@ -454,25 +441,24 @@ class CashTests {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
// Gather 2000 dollars from two different issuers.
|
||||
input(Cash.PROGRAM_ID) { inState }
|
||||
input(Cash.PROGRAM_ID) { inState issuedBy MINI_CORP }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
|
||||
input(Cash.PROGRAM_ID, inState)
|
||||
input(Cash.PROGRAM_ID, inState issuedBy MINI_CORP)
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
// Can't merge them together.
|
||||
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"
|
||||
}
|
||||
// Missing MiniCorp deposit
|
||||
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 works.
|
||||
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)))
|
||||
output(Cash.PROGRAM_ID, inState.copy(owner = AnonymousParty(BOB_PUBKEY)) issuedBy MINI_CORP)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -483,12 +469,11 @@ class CashTests {
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
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) { pounds }
|
||||
output(Cash.PROGRAM_ID) { inState ownedBy AnonymousParty(BOB_PUBKEY) }
|
||||
output(Cash.PROGRAM_ID) { pounds ownedBy AnonymousParty(ALICE_PUBKEY) }
|
||||
command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() }
|
||||
|
||||
input(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(ALICE_PUBKEY))
|
||||
input(Cash.PROGRAM_ID, pounds)
|
||||
output(Cash.PROGRAM_ID, inState ownedBy AnonymousParty(BOB_PUBKEY))
|
||||
output(Cash.PROGRAM_ID, pounds ownedBy AnonymousParty(ALICE_PUBKEY))
|
||||
command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -792,19 +777,17 @@ class CashTests {
|
||||
ledger(mockService) {
|
||||
unverifiedTransaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash") {
|
||||
output(Cash.PROGRAM_ID, "MEGA_CORP cash",
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP
|
||||
)
|
||||
}
|
||||
owner = MEGA_CORP))
|
||||
}
|
||||
|
||||
transaction {
|
||||
attachment(Cash.PROGRAM_ID)
|
||||
input("MEGA_CORP cash")
|
||||
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()
|
||||
}
|
||||
|
||||
@ -814,7 +797,7 @@ class CashTests {
|
||||
input("MEGA_CORP cash")
|
||||
// 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))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
this.fails()
|
||||
|
@ -71,34 +71,33 @@ class ObligationTests {
|
||||
fun trivial() {
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, outState.copy(quantity = 2000.DOLLARS.quantity))
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(CHARLIE.owningKey) { DummyCommandData }
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(CHARLIE.owningKey, DummyCommandData)
|
||||
// Invalid command
|
||||
this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command"
|
||||
}
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(BOB_PUBKEY) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(BOB_PUBKEY, Obligation.Commands.Move())
|
||||
this `fails with` "the owning keys are a subset of the signing keys"
|
||||
}
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP)
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "at least one obligation input"
|
||||
}
|
||||
// Simple reallocation works.
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -109,10 +108,9 @@ class ObligationTests {
|
||||
// Check we can't "move" debt into existence.
|
||||
transaction {
|
||||
attachments(DummyContract.PROGRAM_ID, Obligation.PROGRAM_ID)
|
||||
input(DummyContract.PROGRAM_ID) { DummyState() }
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
|
||||
|
||||
input(DummyContract.PROGRAM_ID, DummyState())
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
|
||||
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.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Issue() }
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Issue())
|
||||
this `fails with` "output states are issued by a command signer"
|
||||
}
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
output(Obligation.PROGRAM_ID) {
|
||||
output(Obligation.PROGRAM_ID,
|
||||
Obligation.State(
|
||||
obligor = MINI_CORP,
|
||||
quantity = 1000.DOLLARS.quantity,
|
||||
beneficiary = CHARLIE,
|
||||
template = megaCorpDollarSettlement
|
||||
)
|
||||
}
|
||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
template = megaCorpDollarSettlement))
|
||||
command(MINI_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
this.verifies()
|
||||
}
|
||||
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.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2))
|
||||
// Move fails: not allowed to summon money.
|
||||
tweak {
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
|
||||
// Issue works.
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -176,29 +171,29 @@ class ObligationTests {
|
||||
// Can't use an issue command to lower the amount.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) }
|
||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity / 2))
|
||||
command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
this `fails with` "output values sum to more than the inputs"
|
||||
}
|
||||
|
||||
// Can't have an issue command that doesn't actually issue money.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState }
|
||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState)
|
||||
command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
this `fails with` ""
|
||||
}
|
||||
|
||||
// Can't have any other commands if we have an issue command (because the issue command overrules them).
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) }
|
||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.amount.quantity * 2))
|
||||
command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
tweak {
|
||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() }
|
||||
command(MEGA_CORP_PUBKEY, Obligation.Commands.Issue())
|
||||
this `fails with` "there is only a single issue command"
|
||||
}
|
||||
this.verifies()
|
||||
@ -352,7 +347,7 @@ class ObligationTests {
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
input("Bob's $1,000,000 obligation to Alice")
|
||||
// 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)
|
||||
this.verifies()
|
||||
}
|
||||
@ -368,8 +363,8 @@ class ObligationTests {
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
input("Bob's $1,000,000 obligation to Alice")
|
||||
input("MegaCorp's $1,000,000 obligation to Bob")
|
||||
output(Obligation.PROGRAM_ID, "change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) }
|
||||
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
||||
output(Obligation.PROGRAM_ID, "change", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB))
|
||||
command(listOf(BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.CLOSE_OUT))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -383,8 +378,8 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
input("Bob's $1,000,000 obligation to Alice")
|
||||
output(Obligation.PROGRAM_ID, "change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) }
|
||||
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
||||
output(Obligation.PROGRAM_ID, "change", oneMillionDollars.splitEvenly(2).first().OBLIGATION between Pair(ALICE, BOB))
|
||||
command(BOB_PUBKEY, Obligation.Commands.Net(NetType.CLOSE_OUT))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "amounts owed on input and output must match"
|
||||
}
|
||||
@ -397,7 +392,7 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
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)
|
||||
this `fails with` "any involved party has signed"
|
||||
}
|
||||
@ -413,7 +408,7 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
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)
|
||||
this.verifies()
|
||||
}
|
||||
@ -428,7 +423,7 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
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)
|
||||
this `fails with` "all involved parties have signed"
|
||||
}
|
||||
@ -441,8 +436,8 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Bob's $1,000,000 obligation to Alice")
|
||||
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) }
|
||||
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
||||
output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE))
|
||||
command(listOf(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -456,8 +451,8 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Bob's $1,000,000 obligation to Alice")
|
||||
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) }
|
||||
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
||||
output(Obligation.PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE))
|
||||
command(listOf(ALICE_PUBKEY, BOB_PUBKEY), Obligation.Commands.Net(NetType.PAYMENT))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "all involved parties have signed"
|
||||
}
|
||||
@ -473,9 +468,9 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
input("Alice's $1,000,000")
|
||||
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) { Cash.Commands.Move(Obligation::class.java) }
|
||||
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, Cash.Commands.Move(Obligation::class.java))
|
||||
attachment(attachment(cashContractBytes.inputStream()))
|
||||
this.verifies()
|
||||
}
|
||||
@ -488,10 +483,10 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
|
||||
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, "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) { Cash.Commands.Move(Obligation::class.java) }
|
||||
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)
|
||||
command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move(Obligation::class.java))
|
||||
attachment(attachment(cashContractBytes.inputStream()))
|
||||
this.verifies()
|
||||
}
|
||||
@ -504,9 +499,9 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID, Cash.PROGRAM_ID)
|
||||
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)
|
||||
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) { Cash.Commands.Move(Obligation::class.java) }
|
||||
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, Cash.Commands.Move(Obligation::class.java))
|
||||
this `fails with` "all inputs are in the normal state"
|
||||
}
|
||||
}
|
||||
@ -518,9 +513,9 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's $1,000,000 obligation to Bob")
|
||||
input("Alice's $1,000,000")
|
||||
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) { Cash.Commands.Move(Obligation::class.java) }
|
||||
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, Cash.Commands.Move(Obligation::class.java))
|
||||
attachment(attachment(cashContractBytes.inputStream()))
|
||||
this `fails with` "amount in settle command"
|
||||
}
|
||||
@ -546,9 +541,9 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input("Alice's 1 FCOJ obligation to Bob")
|
||||
input("Alice's 1 FCOJ")
|
||||
output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) }
|
||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) }
|
||||
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) }
|
||||
output(Obligation.PROGRAM_ID, "Bob's 1 FCOJ", CommodityContract.State(oneUnitFcoj, BOB))
|
||||
command(ALICE_PUBKEY, Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)))
|
||||
command(ALICE_PUBKEY, CommodityContract.Commands.Move(Obligation::class.java))
|
||||
attachment(attachment(commodityContractBytes.inputStream()))
|
||||
verifies()
|
||||
}
|
||||
@ -563,8 +558,8 @@ class ObligationTests {
|
||||
transaction("Settlement") {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
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) }
|
||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED))
|
||||
command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
|
||||
this `fails with` "there is a time-window from the authority"
|
||||
}
|
||||
}
|
||||
@ -575,8 +570,8 @@ class ObligationTests {
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime)
|
||||
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED))
|
||||
command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "the due date has passed"
|
||||
}
|
||||
@ -586,8 +581,8 @@ class ObligationTests {
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime)
|
||||
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||
output(Obligation.PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob", (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED))
|
||||
command(BOB_PUBKEY, Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -600,24 +595,24 @@ class ObligationTests {
|
||||
// Splitting value works.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
tweak {
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
repeat(4) { output(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
repeat(4) { output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 4)) }
|
||||
this.verifies()
|
||||
}
|
||||
// Merging 4 inputs into 2 outputs works.
|
||||
tweak {
|
||||
repeat(4) { input(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) }
|
||||
repeat(4) { input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 4)) }
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
|
||||
this.verifies()
|
||||
}
|
||||
// Merging 2 inputs into 1 works.
|
||||
tweak {
|
||||
input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
|
||||
input(Obligation.PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) }
|
||||
output(Obligation.PROGRAM_ID) { inState }
|
||||
input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
|
||||
input(Obligation.PROGRAM_ID, inState.copy(quantity = inState.quantity / 2))
|
||||
output(Obligation.PROGRAM_ID, inState)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -627,18 +622,16 @@ class ObligationTests {
|
||||
fun zeroSizedValues() {
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
tweak {
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
input(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
input(Obligation.PROGRAM_ID, inState.copy(quantity = 0L))
|
||||
this `fails with` "zero sized inputs"
|
||||
}
|
||||
tweak {
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(quantity = 0L) }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, inState.copy(quantity = 0L))
|
||||
this `fails with` "zero sized outputs"
|
||||
}
|
||||
}
|
||||
@ -649,41 +642,39 @@ class ObligationTests {
|
||||
// Can't change issuer.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { outState `issued by` MINI_CORP }
|
||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, outState `issued by` MINI_CORP)
|
||||
command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't mix currencies.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) }
|
||||
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) }
|
||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, outState.copy(quantity = 80000, template = megaCorpDollarSettlement))
|
||||
output(Obligation.PROGRAM_ID, outState.copy(quantity = 20000, template = megaCorpPoundSettlement))
|
||||
command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
input(Obligation.PROGRAM_ID) {
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
input(Obligation.PROGRAM_ID,
|
||||
inState.copy(
|
||||
quantity = 15000,
|
||||
template = megaCorpPoundSettlement,
|
||||
beneficiary = AnonymousParty(BOB_PUBKEY)
|
||||
)
|
||||
}
|
||||
output(Obligation.PROGRAM_ID) { outState.copy(quantity = 115000) }
|
||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() }
|
||||
beneficiary = AnonymousParty(BOB_PUBKEY)))
|
||||
output(Obligation.PROGRAM_ID, outState.copy(quantity = 115000))
|
||||
command(MINI_CORP_PUBKEY, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Can't have superfluous input states from different issuers.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP }
|
||||
output(Obligation.PROGRAM_ID) { outState }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP)
|
||||
output(Obligation.PROGRAM_ID, outState)
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
}
|
||||
@ -693,21 +684,20 @@ class ObligationTests {
|
||||
// Single input/output straightforward case.
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
output(Obligation.PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
output(Obligation.PROGRAM_ID, outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity))
|
||||
tweak {
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)))
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
tweak {
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -720,21 +710,15 @@ class ObligationTests {
|
||||
// Multi-product case.
|
||||
transaction {
|
||||
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 = 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) }
|
||||
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)))
|
||||
input(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)))
|
||||
output(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity))
|
||||
output(Obligation.PROGRAM_ID, inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity))
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
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"
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -745,27 +729,26 @@ class ObligationTests {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
|
||||
// Gather 2000 dollars from two different issuers.
|
||||
input(Obligation.PROGRAM_ID) { inState }
|
||||
input(Obligation.PROGRAM_ID) { inState `issued by` MINI_CORP }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState)
|
||||
input(Obligation.PROGRAM_ID, inState `issued by` MINI_CORP)
|
||||
// Can't merge them together.
|
||||
tweak {
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L))
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
// Missing MiniCorp deposit
|
||||
tweak {
|
||||
output(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() }
|
||||
output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)))
|
||||
output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)))
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this `fails with` "the amounts balance"
|
||||
}
|
||||
|
||||
// This works.
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) }
|
||||
output(Obligation.PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP }
|
||||
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||
output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)))
|
||||
output(Obligation.PROGRAM_ID, inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP)
|
||||
command(CHARLIE.owningKey, Obligation.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -776,12 +759,11 @@ class ObligationTests {
|
||||
transaction {
|
||||
attachments(Obligation.PROGRAM_ID)
|
||||
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) { pounds }
|
||||
output(Obligation.PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) }
|
||||
output(Obligation.PROGRAM_ID) { pounds `owned by` CHARLIE }
|
||||
command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() }
|
||||
|
||||
input(Obligation.PROGRAM_ID, inState `owned by` CHARLIE)
|
||||
input(Obligation.PROGRAM_ID, pounds)
|
||||
output(Obligation.PROGRAM_ID, inState `owned by` AnonymousParty(BOB_PUBKEY))
|
||||
output(Obligation.PROGRAM_ID, pounds `owned by` CHARLIE)
|
||||
command(listOf(CHARLIE.owningKey, BOB_PUBKEY), Obligation.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ package net.corda.nodeapi
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
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.core.remoting.impl.netty.NettyConnectorFactory
|
||||
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
|
||||
|
@ -1,6 +1,6 @@
|
||||
@file:JvmName("ArtemisUtils")
|
||||
|
||||
package net.corda.nodeapi
|
||||
package net.corda.nodeapi.internal
|
||||
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
@ -1,6 +1,6 @@
|
||||
@file:JvmName("ConfigUtilities")
|
||||
|
||||
package net.corda.nodeapi.config
|
||||
package net.corda.nodeapi.internal.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
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")
|
@ -1,4 +1,4 @@
|
||||
package net.corda.nodeapi.config
|
||||
package net.corda.nodeapi.internal.config
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import java.nio.file.Path
|
@ -1,7 +1,4 @@
|
||||
package net.corda.nodeapi
|
||||
|
||||
import net.corda.nodeapi.config.OldConfig
|
||||
import net.corda.nodeapi.config.toConfig
|
||||
package net.corda.nodeapi.internal.config
|
||||
|
||||
data class User(
|
||||
@OldConfig("user")
|
@ -1,4 +1,4 @@
|
||||
package net.corda.nodeapi.config
|
||||
package net.corda.nodeapi.internal.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory.empty
|
@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.NodeStartup
|
||||
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.common.internal.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.driver.driver
|
||||
|
@ -8,7 +8,7 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
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.BOB
|
||||
import net.corda.testing.chooseIdentity
|
||||
|
@ -13,7 +13,7 @@ import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
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.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
|
@ -9,7 +9,7 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
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.driver.driver
|
||||
import org.bouncycastle.util.io.Streams
|
||||
|
@ -13,7 +13,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
|
@ -49,7 +49,7 @@ class NodeInfoWatcherTest {
|
||||
fun start() {
|
||||
val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -4,15 +4,15 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.internal.InputStreamAndHash
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.BOB
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.aliceAndBob
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.dummyCommand
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -65,15 +65,16 @@ class LargeTransactionsTest {
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||
val (alice, _) = aliceAndBob()
|
||||
alice.useRPC {
|
||||
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
||||
val hash2 = it.uploadAttachment(bigFile2.inputStream)
|
||||
val hash3 = it.uploadAttachment(bigFile3.inputStream)
|
||||
val hash4 = it.uploadAttachment(bigFile4.inputStream)
|
||||
val rpcUser = User("admin", "admin", setOf("ALL"))
|
||||
val (alice, _) = listOf(ALICE_NAME, BOB_NAME).map { startNode(providedName = it, rpcUsers = listOf(rpcUser)) }.transpose().getOrThrow()
|
||||
alice.rpcClientToNode().use(rpcUser.username, rpcUser.password) {
|
||||
val hash1 = it.proxy.uploadAttachment(bigFile1.inputStream)
|
||||
val hash2 = it.proxy.uploadAttachment(bigFile2.inputStream)
|
||||
val hash3 = it.proxy.uploadAttachment(bigFile3.inputStream)
|
||||
val hash4 = it.proxy.uploadAttachment(bigFile4.inputStream)
|
||||
assertEquals(hash1, bigFile1.sha256)
|
||||
// Should not throw any exceptions.
|
||||
it.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.get()
|
||||
it.proxy.startFlow(::SendLargeTransactionFlow, hash1, hash2, hash3, hash4).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.PEER_USER
|
||||
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.testing.MEGA_CORP
|
||||
import net.corda.testing.MINI_CORP
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.services.messaging
|
||||
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.testing.messaging.SimpleMQClient
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -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.PEERS_PREFIX
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.internal.NodeBasedTest
|
||||
import net.corda.testing.messaging.SimpleMQClient
|
||||
|
@ -19,7 +19,7 @@ import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.driver
|
||||
import org.junit.Assume.assumeFalse
|
||||
|
@ -66,6 +66,7 @@ import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.Scheduler
|
||||
import java.io.IOException
|
||||
import java.io.NotSerializableException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
@ -232,7 +233,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
|
||||
NodeInfoWatcher(configuration.baseDirectory, Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
||||
NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
||||
networkMapClient)
|
||||
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) {
|
||||
InteractiveShell.startShell(configuration, rpcOps, userService, _services.identityService, _services.database)
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.serialization.*
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import rx.schedulers.Schedulers
|
||||
import java.time.Clock
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.management.ObjectName
|
||||
@ -46,7 +47,7 @@ import kotlin.system.exitProcess
|
||||
*/
|
||||
open class Node(configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
val initialiseSerialization: Boolean = true,
|
||||
private val initialiseSerialization: Boolean = true,
|
||||
cordappLoader: CordappLoader = makeCordappLoader(configuration)
|
||||
) : AbstractNode(configuration, createClock(configuration), versionInfo, cordappLoader) {
|
||||
companion object {
|
||||
@ -299,6 +300,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
return started
|
||||
}
|
||||
|
||||
override fun getRxIoScheduler() = Schedulers.io()!!
|
||||
private fun initialiseSerialization() {
|
||||
val classloader = cordappLoader.appClassLoader
|
||||
nodeSerializationEnv = SerializationEnvironmentImpl(
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.node.services
|
||||
|
||||
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]
|
||||
|
@ -8,7 +8,7 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SignatureScheme
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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 org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||
|
@ -6,9 +6,9 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.node.services.messaging.CertificateChainCheckPolicy
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
@ -6,7 +6,7 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
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.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||
|
||||
|
@ -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.ArtemisPeerAddress
|
||||
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.management.ActiveMQServerControl
|
||||
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.security.Role
|
||||
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.settings.HierarchicalRepository
|
||||
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
|
||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings
|
||||
import org.apache.activemq.artemis.spi.core.remoting.*
|
||||
@ -139,8 +142,8 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
// Artemis IO errors
|
||||
@Throws(IOException::class, KeyStoreException::class)
|
||||
private fun configureAndStartServer() {
|
||||
val artemisConfig = createArtemisConfig()
|
||||
val securityManager = createArtemisSecurityManager()
|
||||
val (artemisConfig, securityPlugin) = createArtemisConfig()
|
||||
val securityManager = createArtemisSecurityManager(securityPlugin)
|
||||
activeMQServer = ActiveMQServerImpl(artemisConfig, securityManager).apply {
|
||||
// Throw any exceptions which are detected during startup
|
||||
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"
|
||||
bindingsDirectory = (artemisDir / "bindings").toString()
|
||||
journalDirectory = (artemisDir / "journal").toString()
|
||||
@ -208,8 +211,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
|
||||
}
|
||||
)
|
||||
configureAddressSecurity()
|
||||
}
|
||||
}.configureAddressSecurity()
|
||||
|
||||
private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration {
|
||||
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.
|
||||
* 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)
|
||||
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))
|
||||
@ -236,13 +238,22 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
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
|
||||
// and stealing RPC responses.
|
||||
for ((username) in userService.users) {
|
||||
securityRoles["${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#"] = setOf(
|
||||
nodeInternalRole,
|
||||
restrictedRole("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username", consume = true, createNonDurableQueue = true, deleteNonDurableQueue = true))
|
||||
val rolesAdderOnLogin = RolesAdderOnLogin { username ->
|
||||
Pair(
|
||||
"${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username.#",
|
||||
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_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,
|
||||
@ -253,7 +264,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
}
|
||||
|
||||
@Throws(IOException::class, KeyStoreException::class)
|
||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||
private fun createArtemisSecurityManager(loginListener: LoginListener): ActiveMQJAASSecurityManager {
|
||||
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
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 fun getAppConfigurationEntry(name: String): Array<AppConfigurationEntry> {
|
||||
val options = mapOf(
|
||||
LoginListener::javaClass.name to loginListener,
|
||||
RPCUserService::class.java.name to userService,
|
||||
NodeLoginModule.CERT_CHAIN_CHECKS_OPTION_NAME to certChecks)
|
||||
return arrayOf(AppConfigurationEntry(name, REQUIRED, options))
|
||||
@ -546,6 +558,7 @@ class NodeLoginModule : LoginModule {
|
||||
private lateinit var subject: Subject
|
||||
private lateinit var callbackHandler: CallbackHandler
|
||||
private lateinit var userService: RPCUserService
|
||||
private lateinit var loginListener: LoginListener
|
||||
private lateinit var peerCertCheck: CertificateChainCheckPolicy.Check
|
||||
private lateinit var nodeCertCheck: CertificateChainCheckPolicy.Check
|
||||
private lateinit var verifierCertCheck: CertificateChainCheckPolicy.Check
|
||||
@ -555,6 +568,7 @@ class NodeLoginModule : LoginModule {
|
||||
this.subject = subject
|
||||
this.callbackHandler = callbackHandler
|
||||
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])
|
||||
peerCertCheck = certChainChecks[PEER_ROLE]!!
|
||||
nodeCertCheck = certChainChecks[NODE_ROLE]!!
|
||||
@ -622,6 +636,7 @@ class NodeLoginModule : LoginModule {
|
||||
// TODO Retrieve client IP address to include in exception message
|
||||
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("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$username") // This enables the RPC client to receive responses
|
||||
return username
|
||||
@ -676,3 +691,40 @@ class NodeLoginModule : LoginModule {
|
||||
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
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
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.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||
|
@ -30,6 +30,7 @@ import net.corda.node.services.RPCUserService
|
||||
import net.corda.node.services.logging.pushToLoggingContext
|
||||
import net.corda.nodeapi.*
|
||||
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.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||
|
@ -11,7 +11,7 @@ import net.corda.node.utilities.*
|
||||
import net.corda.nodeapi.VerifierApi
|
||||
import net.corda.nodeapi.VerifierApi.VERIFICATION_REQUESTS_QUEUE_NAME
|
||||
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.SimpleString
|
||||
import org.apache.activemq.artemis.api.core.client.*
|
||||
|
@ -12,7 +12,6 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.nodeapi.internal.NodeInfoFilesCopier
|
||||
import rx.Observable
|
||||
import rx.Scheduler
|
||||
import rx.schedulers.Schedulers
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
@ -31,9 +30,8 @@ import kotlin.streams.toList
|
||||
*/
|
||||
// TODO: Use NIO watch service instead?
|
||||
class NodeInfoWatcher(private val nodePath: Path,
|
||||
private val pollInterval: Duration = 5.seconds,
|
||||
private val scheduler: Scheduler = Schedulers.io()) {
|
||||
|
||||
private val scheduler: Scheduler,
|
||||
private val pollInterval: Duration = 5.seconds) {
|
||||
private val nodeInfoDirectory = nodePath / CordformNode.NODE_INFO_DIRECTORY
|
||||
private val processedNodeInfoFiles = mutableSetOf<Path>()
|
||||
private val _processedNodeInfoHashes = mutableSetOf<SecureHash>()
|
||||
|
@ -29,8 +29,8 @@ import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.node.services.config.RaftConfig
|
||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||
import net.corda.nodeapi.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||
import java.nio.file.Path
|
||||
|
@ -653,13 +653,13 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// wants to sell to Bob.
|
||||
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
// 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 2", notary = notary) { 1000.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, contractState = 1000.DOLLARS.CASH issuedBy issuer ownedBy interimOwner)
|
||||
if (!withError) {
|
||||
command(issuer.party.owningKey) { Cash.Commands.Issue() }
|
||||
command(issuer.party.owningKey, Cash.Commands.Issue())
|
||||
} else {
|
||||
// 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)
|
||||
if (withError) {
|
||||
@ -672,16 +672,16 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
// Bob gets some cash onto the ledger from BoE
|
||||
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
input("elbonian money 1")
|
||||
output(Cash.PROGRAM_ID, "bob cash 1", notary = notary) { 800.DOLLARS.CASH issuedBy issuer ownedBy owner }
|
||||
command(interimOwner.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, "bob cash 1", notary = notary, contractState = 800.DOLLARS.CASH issuedBy issuer ownedBy owner)
|
||||
command(interimOwner.owningKey, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
input("elbonian money 2")
|
||||
output(Cash.PROGRAM_ID, "bob cash 2", notary = notary) { 300.DOLLARS.CASH issuedBy issuer ownedBy owner }
|
||||
output(Cash.PROGRAM_ID, notary = notary) { 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner } // Change output.
|
||||
command(interimOwner.owningKey) { Cash.Commands.Move() }
|
||||
output(Cash.PROGRAM_ID, "bob cash 2", notary = notary, contractState = 300.DOLLARS.CASH issuedBy issuer ownedBy owner)
|
||||
output(Cash.PROGRAM_ID, notary = notary, contractState = 700.DOLLARS.CASH issuedBy issuer ownedBy interimOwner) // Change output.
|
||||
command(interimOwner.owningKey, Cash.Commands.Move())
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
@ -697,10 +697,9 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
attachmentID: SecureHash?,
|
||||
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
||||
val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary) {
|
||||
CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)
|
||||
}
|
||||
command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() }
|
||||
output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary,
|
||||
contractState = CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days))
|
||||
command(issuer.party.owningKey, CommercialPaper.Commands.Issue())
|
||||
if (!withError)
|
||||
timeWindow(time = TEST_TX_TIME)
|
||||
if (attachmentID != null)
|
||||
|
@ -1,7 +1,7 @@
|
||||
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.junit.Test
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.node.services.config
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.corda.nodeapi.config.toProperties
|
||||
import net.corda.nodeapi.internal.config.toProperties
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
@ -12,9 +12,7 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.node.internal.FlowStarterImpl
|
||||
@ -44,7 +42,6 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.nio.file.Paths
|
||||
import java.security.PublicKey
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.CountDownLatch
|
||||
@ -105,13 +102,9 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
doReturn(MonitoringService(MetricRegistry())).whenever(it).monitoringService
|
||||
doReturn(validatedTransactions).whenever(it).validatedTransactions
|
||||
doReturn(NetworkMapCacheImpl(MockNetworkMapCache(database), identityService)).whenever(it).networkMapCache
|
||||
doCallRealMethod().whenever(it).signInitialTransaction(any(), any<PublicKey>())
|
||||
doReturn(myInfo).whenever(it).myInfo
|
||||
doReturn(kms).whenever(it).keyManagementService
|
||||
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(this@NodeSchedulerServiceTest).whenever(it).testReference
|
||||
|
||||
|
@ -52,7 +52,7 @@ class NetworkMapUpdaterTest {
|
||||
val networkMapClient = mock<NetworkMapClient>()
|
||||
|
||||
val scheduler = TestScheduler()
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler)
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
||||
|
||||
// Publish node info for the first time.
|
||||
@ -101,7 +101,7 @@ class NetworkMapUpdaterTest {
|
||||
}
|
||||
|
||||
val scheduler = TestScheduler()
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler)
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
||||
|
||||
// Test adding new node.
|
||||
@ -155,7 +155,7 @@ class NetworkMapUpdaterTest {
|
||||
}
|
||||
|
||||
val scheduler = TestScheduler()
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler)
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
||||
|
||||
// Add all nodes.
|
||||
@ -199,7 +199,7 @@ class NetworkMapUpdaterTest {
|
||||
val networkMapCache = getMockNetworkMapCache()
|
||||
|
||||
val scheduler = TestScheduler()
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler = scheduler)
|
||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, null)
|
||||
|
||||
// Not subscribed yet.
|
||||
|
@ -4,7 +4,7 @@ import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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_B
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.attachmentdemo
|
||||
|
||||
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_B
|
||||
import net.corda.testing.driver.driver
|
||||
|
@ -10,7 +10,7 @@ import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
||||
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.driver.driver
|
||||
import org.junit.Test
|
||||
|
@ -12,7 +12,7 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
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.internal.demorun.*
|
||||
import java.util.*
|
||||
|
@ -385,8 +385,8 @@ class IRSTests {
|
||||
return ledger {
|
||||
transaction("Agreement") {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() }
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
output(IRS_PROGRAM_ID, "irs post agreement", singleIRS())
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -395,17 +395,14 @@ class IRSTests {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
input("irs post agreement")
|
||||
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.fixedLeg,
|
||||
postAgreement.floatingLeg,
|
||||
postAgreement.calculation.applyFixing(ld, FixedRate(RatioUnit(bd))),
|
||||
postAgreement.common
|
||||
)
|
||||
}
|
||||
command(ORACLE_PUBKEY) {
|
||||
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd))
|
||||
}
|
||||
postAgreement.common))
|
||||
command(ORACLE_PUBKEY,
|
||||
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -419,7 +416,7 @@ class IRSTests {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
input(IRS_PROGRAM_ID, 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)
|
||||
this `fails with` "There are no in states for an agreement"
|
||||
}
|
||||
@ -432,7 +429,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
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)
|
||||
this `fails with` "There are events in the fix schedule"
|
||||
}
|
||||
@ -445,7 +442,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
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)
|
||||
this `fails with` "There are events in the float schedule"
|
||||
}
|
||||
@ -457,7 +454,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
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)
|
||||
this `fails with` "All notionals must be non zero"
|
||||
}
|
||||
@ -465,7 +462,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
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)
|
||||
this `fails with` "All notionals must be non zero"
|
||||
}
|
||||
@ -478,7 +475,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The fixed leg rate must be positive"
|
||||
}
|
||||
@ -494,7 +491,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The currency of the notionals must be the same"
|
||||
}
|
||||
@ -507,7 +504,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "All leg notionals must be the same"
|
||||
}
|
||||
@ -520,7 +517,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS1)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The effective date is before the termination date for the fixed leg"
|
||||
}
|
||||
@ -529,7 +526,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS2)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The effective date is before the termination date for the floating leg"
|
||||
}
|
||||
@ -543,7 +540,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS3)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The termination dates are aligned"
|
||||
}
|
||||
@ -553,7 +550,7 @@ class IRSTests {
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, modifiedIRS4)
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this `fails with` "The effective dates are aligned"
|
||||
}
|
||||
@ -567,8 +564,8 @@ class IRSTests {
|
||||
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() }
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
output(IRS_PROGRAM_ID, "irs post agreement", singleIRS())
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -585,9 +582,8 @@ class IRSTests {
|
||||
|
||||
// Templated tweak for reference. A corrent fixing applied should be ok
|
||||
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)
|
||||
output(IRS_PROGRAM_ID, newIRS)
|
||||
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
|
||||
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)
|
||||
output(IRS_PROGRAM_ID, oldIRS)
|
||||
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)
|
||||
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)
|
||||
|
||||
val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.toList()[1]
|
||||
val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey]
|
||||
val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY")))
|
||||
|
||||
output(IRS_PROGRAM_ID) {
|
||||
output(IRS_PROGRAM_ID,
|
||||
newIRS.copy(
|
||||
newIRS.fixedLeg,
|
||||
newIRS.floatingLeg,
|
||||
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
|
||||
Pair(firstResetKey, modifiedFirstResetValue))),
|
||||
newIRS.common
|
||||
)
|
||||
}
|
||||
newIRS.common))
|
||||
this `fails with` "There is only one change in the IRS floating leg payment schedule"
|
||||
}
|
||||
|
||||
// This tests modifies the payment currency for the fixing
|
||||
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)
|
||||
|
||||
val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key }
|
||||
val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY")))
|
||||
|
||||
output(IRS_PROGRAM_ID) {
|
||||
output(IRS_PROGRAM_ID,
|
||||
newIRS.copy(
|
||||
newIRS.fixedLeg,
|
||||
newIRS.floatingLeg,
|
||||
newIRS.calculation.copy(floatingLegPaymentSchedule = newIRS.calculation.floatingLegPaymentSchedule.plus(
|
||||
Pair(latestReset.key, modifiedLatestResetValue))),
|
||||
newIRS.common
|
||||
)
|
||||
}
|
||||
newIRS.common))
|
||||
this `fails with` "The fix payment has the same currency as the notional"
|
||||
}
|
||||
}
|
||||
@ -660,31 +650,27 @@ class IRSTests {
|
||||
return ledger {
|
||||
transaction("Agreement") {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, "irs post agreement1") {
|
||||
output(IRS_PROGRAM_ID, "irs post agreement1",
|
||||
irs.copy(
|
||||
irs.fixedLeg,
|
||||
irs.floatingLeg,
|
||||
irs.calculation,
|
||||
irs.common.copy(tradeID = "t1")
|
||||
)
|
||||
}
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
irs.common.copy(tradeID = "t1")))
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
transaction("Agreement") {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
output(IRS_PROGRAM_ID, "irs post agreement2") {
|
||||
output(IRS_PROGRAM_ID, "irs post agreement2",
|
||||
irs.copy(
|
||||
linearId = UniqueIdentifier("t2"),
|
||||
fixedLeg = irs.fixedLeg,
|
||||
floatingLeg = irs.floatingLeg,
|
||||
calculation = irs.calculation,
|
||||
common = irs.common.copy(tradeID = "t2")
|
||||
)
|
||||
}
|
||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||
common = irs.common.copy(tradeID = "t2")))
|
||||
command(MEGA_CORP_PUBKEY, InterestRateSwap.Commands.Agree())
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
@ -694,27 +680,21 @@ class IRSTests {
|
||||
input("irs post agreement1")
|
||||
input("irs post agreement2")
|
||||
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.fixedLeg,
|
||||
postAgreement1.floatingLeg,
|
||||
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>()
|
||||
output(IRS_PROGRAM_ID, "irs post first fixing2") {
|
||||
output(IRS_PROGRAM_ID, "irs post first fixing2",
|
||||
postAgreement2.copy(
|
||||
postAgreement2.fixedLeg,
|
||||
postAgreement2.floatingLeg,
|
||||
postAgreement2.calculation.applyFixing(ld1, FixedRate(RatioUnit(bd1))),
|
||||
postAgreement2.common.copy(tradeID = "t2")
|
||||
)
|
||||
}
|
||||
|
||||
command(ORACLE_PUBKEY) {
|
||||
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1))
|
||||
}
|
||||
postAgreement2.common.copy(tradeID = "t2")))
|
||||
command(ORACLE_PUBKEY,
|
||||
InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1)))
|
||||
timeWindow(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import net.corda.finance.plugin.registerFinanceJSONMappers
|
||||
import net.corda.irs.contract.InterestRateSwap
|
||||
import net.corda.irs.web.IrsDemoWebApplication
|
||||
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.testing.*
|
||||
import net.corda.testing.http.HttpApi
|
||||
|
@ -4,7 +4,7 @@ import net.corda.cordform.CordformContext
|
||||
import net.corda.cordform.CordformDefinition
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
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.BOB
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
|
@ -8,7 +8,7 @@ import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
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.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_BANK_B
|
||||
|
@ -4,7 +4,7 @@ import net.corda.core.internal.div
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
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.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_BANK_B
|
||||
|
@ -9,7 +9,7 @@ import net.corda.core.internal.read
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
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.node.MockNetwork
|
||||
import org.junit.Ignore
|
||||
|
@ -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)
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
package net.corda.testing
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doCallRealMethod
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
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.node.services.config.NodeConfiguration
|
||||
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.Companion.makeTestDataSourceProperties
|
||||
import java.nio.file.Path
|
||||
@ -74,11 +73,6 @@ fun testNodeConfiguration(
|
||||
doReturn(5).whenever(it).messageRedeliveryDelaySeconds
|
||||
doReturn(5.seconds.toMillis()).whenever(it).additionalNodeInfoPollingFrequencyMsec
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ import net.corda.core.utilities.*
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeStartup
|
||||
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.config.*
|
||||
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.NetworkRegistrationHelper
|
||||
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.toConfig
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.NotaryInfo
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
@ -46,6 +45,7 @@ import net.corda.testing.*
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.DriverDSL.ClusterType.*
|
||||
import net.corda.testing.internal.InProcessNode
|
||||
import net.corda.testing.internal.ProcessUtilities
|
||||
import net.corda.testing.node.ClusterSpec
|
||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
@ -345,7 +345,7 @@ data class NodeParameters(
|
||||
* available from [DriverDSLExposedInterface.notaryHandles]. Defaults to a simple validating notary.
|
||||
* @param compatibilityZoneURL if not null each node is started once in registration mode (which makes the node register and quit),
|
||||
* and then re-starts the node with the given parameters.
|
||||
* @param rootCertificate if not null every time a node is started for registration that certificate is written on disk
|
||||
* @param rootCertificate if not null every time a node is started for registration that certificate is written on disk
|
||||
* @param dsl The dsl itself.
|
||||
* @return The value returned in the [dsl] closure.
|
||||
*/
|
||||
@ -1120,12 +1120,7 @@ class DriverDSL(
|
||||
// Write node.conf
|
||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||
// TODO pass the version in?
|
||||
val node = Node(
|
||||
nodeConf,
|
||||
MOCK_VERSION_INFO,
|
||||
initialiseSerialization = false,
|
||||
cordappLoader = CordappLoader.createDefaultWithTestPackages(nodeConf, cordappPackages))
|
||||
.start()
|
||||
val node = InProcessNode(nodeConf, MOCK_VERSION_INFO, cordappPackages).start()
|
||||
val nodeThread = thread(name = nodeConf.myLegalName.organisation) {
|
||||
node.internals.run()
|
||||
}
|
||||
|
@ -7,25 +7,28 @@ import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.StartedNode
|
||||
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.configOf
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
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.nodeapi.internal.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.addressMustNotBeBoundFuture
|
||||
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.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
@ -100,11 +103,7 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
|
||||
val parsedConfig = config.parseAsNodeConfiguration()
|
||||
defaultNetworkParameters.install(baseDirectory)
|
||||
val node = Node(
|
||||
parsedConfig,
|
||||
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
|
||||
initialiseSerialization = false,
|
||||
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
|
||||
val node = InProcessNode(parsedConfig, MOCK_VERSION_INFO.copy(platformVersion = platformVersion), cordappPackages).start()
|
||||
nodes += node
|
||||
ensureAllNetworkMapCachesHaveAllNodeInfos()
|
||||
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 }
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import net.corda.node.services.messaging.RPCServerConfiguration
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
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.testing.driver.*
|
||||
import net.corda.testing.node.NotarySpec
|
||||
|
@ -6,8 +6,8 @@ import net.corda.cordform.CordformDefinition
|
||||
import net.corda.cordform.CordformNode
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.toConfig
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
|
||||
fun CordformDefinition.node(configure: CordformNode.() -> Unit) {
|
||||
addNode { cordformNode -> cordformNode.configure() }
|
||||
|
@ -42,12 +42,14 @@ import net.corda.nodeapi.internal.NotaryInfo
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||
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.makeTestDataSourceProperties
|
||||
import net.corda.testing.setGlobalSerialization
|
||||
import net.corda.testing.testNodeConfiguration
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.apache.sshd.common.util.security.SecurityUtils
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
import java.math.BigInteger
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
@ -267,6 +269,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
||||
return started
|
||||
}
|
||||
|
||||
override fun getRxIoScheduler() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
|
||||
private fun advertiseNodeToNetwork(newNode: StartedNode<MockNode>) {
|
||||
mockNet.nodes
|
||||
.mapNotNull { it.started }
|
||||
|
@ -2,7 +2,7 @@ package net.corda.testing.node
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.node.services.config.VerifierType
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
|
||||
data class NotarySpec(
|
||||
val name: CordaX500Name,
|
||||
|
@ -6,7 +6,7 @@ import com.typesafe.config.ConfigRenderOptions
|
||||
import com.typesafe.config.ConfigValue
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
|
||||
class NodeConfig(
|
||||
val legalName: CordaX500Name,
|
||||
|
@ -16,7 +16,7 @@ import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER
|
||||
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.CertificateType
|
||||
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 org.mockito.Mockito.mock
|
||||
import org.mockito.internal.stubbing.answers.ThrowsException
|
||||
import java.lang.reflect.Modifier
|
||||
import java.nio.file.Files
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
@ -180,11 +181,16 @@ class UndefinedMockBehaviorException(message: String) : RuntimeException(message
|
||||
|
||||
inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java)
|
||||
/**
|
||||
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all methods.
|
||||
* @param T the type to mock. Note if you want to use [com.nhaarman.mockito_kotlin.doCallRealMethod] on a Kotlin interface,
|
||||
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all abstract methods,
|
||||
* 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.
|
||||
*/
|
||||
fun <T> rigorousMock(clazz: Class<T>): T = mock(clazz) {
|
||||
// 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)
|
||||
if (Modifier.isAbstract(it.method.modifiers)) {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ data class TestTransactionDSLInterpreter private constructor(
|
||||
transactionBuilder.addInputState(StateAndRef(state, stateRef))
|
||||
}
|
||||
|
||||
override fun _output(contractClassName: ContractClassName,
|
||||
override fun output(contractClassName: ContractClassName,
|
||||
label: String?,
|
||||
notary: Party,
|
||||
encumbrance: Int?,
|
||||
@ -115,7 +115,7 @@ data class TestTransactionDSLInterpreter private constructor(
|
||||
transactionBuilder.addAttachment(attachmentId)
|
||||
}
|
||||
|
||||
override fun _command(signers: List<PublicKey>, commandData: CommandData) {
|
||||
override fun command(signers: List<PublicKey>, commandData: CommandData) {
|
||||
val command = Command(commandData, signers)
|
||||
transactionBuilder.addCommand(command)
|
||||
}
|
||||
|
@ -35,12 +35,12 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
||||
* @param contractState The state itself.
|
||||
* @param contractClassName The class name of the contract that verifies this state.
|
||||
*/
|
||||
fun _output(contractClassName: ContractClassName,
|
||||
label: String?,
|
||||
notary: Party,
|
||||
encumbrance: Int?,
|
||||
attachmentConstraint: AttachmentConstraint,
|
||||
contractState: ContractState)
|
||||
fun output(contractClassName: ContractClassName,
|
||||
label: String?,
|
||||
notary: Party,
|
||||
encumbrance: Int?,
|
||||
attachmentConstraint: AttachmentConstraint,
|
||||
contractState: ContractState)
|
||||
|
||||
/**
|
||||
* Adds an [Attachment] reference to the transaction.
|
||||
@ -53,7 +53,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
||||
* @param signers The signer public keys.
|
||||
* @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.
|
||||
@ -74,10 +74,10 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
||||
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.
|
||||
* @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)
|
||||
|
||||
@ -88,49 +88,51 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
|
||||
*/
|
||||
fun input(contractClassName: ContractClassName, state: ContractState) {
|
||||
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)
|
||||
}
|
||||
|
||||
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? = null,
|
||||
notary: Party = DUMMY_NOTARY,
|
||||
encumbrance: Int? = null,
|
||||
attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint,
|
||||
contractStateClosure: () -> ContractState) =
|
||||
_output(contractClassName, label, notary, encumbrance, attachmentConstraint, contractStateClosure())
|
||||
fun output(contractClassName: ContractClassName, label: String, notary: Party, contractState: ContractState) =
|
||||
output(contractClassName, label, notary, null, AutomaticHashConstraint, contractState)
|
||||
|
||||
/**
|
||||
* Adds a labelled output to the transaction.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun output(contractClassName: ContractClassName, label: String, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) =
|
||||
_output(contractClassName, label, DUMMY_NOTARY, null, attachmentConstraint, contractState)
|
||||
fun output(contractClassName: ContractClassName, label: String, encumbrance: Int, contractState: ContractState) =
|
||||
output(contractClassName, label, DUMMY_NOTARY, encumbrance, AutomaticHashConstraint, 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.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun output(contractClassName: ContractClassName, contractState: ContractState, attachmentConstraint: AttachmentConstraint = AutomaticHashConstraint) =
|
||||
_output(contractClassName, null, DUMMY_NOTARY, null, attachmentConstraint, contractState)
|
||||
fun output(contractClassName: ContractClassName, notary: Party, contractState: ContractState) =
|
||||
output(contractClassName, null, notary, null, AutomaticHashConstraint, 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.
|
||||
*/
|
||||
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) =
|
||||
_command(listOf(*signers), commandDataClosure())
|
||||
|
||||
/**
|
||||
* Adds a command to the transaction.
|
||||
*/
|
||||
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData)
|
||||
fun command(signer: PublicKey, commandData: CommandData) = command(listOf(signer), commandData)
|
||||
|
||||
/**
|
||||
* Sets the [TimeWindow] of the transaction.
|
||||
@ -142,7 +144,7 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
|
||||
timeWindow(TimeWindow.withTolerance(time, tolerance))
|
||||
|
||||
/**
|
||||
* @see TransactionDSLInterpreter._contractAttachment
|
||||
* @see TransactionDSLInterpreter._attachment
|
||||
*/
|
||||
fun attachment(contractClassName: ContractClassName) = _attachment(contractClassName)
|
||||
|
||||
|
@ -5,7 +5,7 @@ import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.ConnectionDirection
|
||||
import net.corda.nodeapi.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.testing.configureTestSSL
|
||||
import org.apache.activemq.artemis.api.core.client.*
|
||||
|
||||
|
@ -3,7 +3,7 @@ package net.corda.demobench.model
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import tornadofx.*
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
|
@ -6,8 +6,8 @@ import net.corda.core.internal.copyToDirectory
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.toConfig
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
|
@ -5,8 +5,8 @@ import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.config.toConfig
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.webserver.WebServerConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
@ -2,7 +2,7 @@ package net.corda.demobench.model
|
||||
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
@ -21,7 +21,7 @@ import net.corda.finance.flows.*
|
||||
import net.corda.finance.flows.CashExitFlow.ExitRequest
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest
|
||||
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.BOB
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.corda.loadtest
|
||||
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user