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

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

137
CONTRIBUTORS.md Normal file
View File

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

View File

@ -28,7 +28,7 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,11 +122,11 @@ handling, and ensures the Corda service is run at boot.
9. Provision the required certificates to your node. Contact the network permissioning service or see
: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:

View 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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,12 @@ Start the nodes with ``runnodes`` by running the following command from the root
.. warn:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may
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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,8 +27,7 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() {
// 2) The window function accumulated column (`total`) does not include the current row (starts from 0) and cannot
// 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)
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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.NodeHandle
import net.corda.testing.driver.driver

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import net.corda.core.internal.*
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.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

View File

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

View File

@ -22,8 +22,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NOTIFICATI
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2P_QUEUE
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.nodeapi.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

View File

@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEER_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,6 @@
package net.corda.testing
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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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