├── .gitignore
├── 010-empty-project
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── template
│ │ │ │ ├── contracts
│ │ │ │ └── TemplateContract.java
│ │ │ │ └── states
│ │ │ │ └── TemplateState.java
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── template
│ │ │ ├── contracts
│ │ │ └── ExampleContract.kt
│ │ │ └── states
│ │ │ └── ExampleState.kt
│ │ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── contracts
│ │ │ └── ContractTests.java
│ │ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── contracts
│ │ └── ContractTestsK.kt
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── flows
│ │ │ ├── Initiator.java
│ │ │ └── Responder.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── Flows.kt
│ └── test
│ ├── java
│ └── com
│ │ └── template
│ │ ├── FlowTests.java
│ │ └── NodeDriver.java
│ └── kotlin
│ └── com
│ └── template
│ ├── FlowTestsK.kt
│ └── NodeDriverK.kt
├── 020-first-token
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── template
│ │ │ │ ├── contracts
│ │ │ │ └── TokenContract.java
│ │ │ │ └── states
│ │ │ │ ├── TokenState.java
│ │ │ │ └── TokenStateUtilities.java
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── template
│ │ │ ├── contracts
│ │ │ └── TokenContractK.kt
│ │ │ └── states
│ │ │ ├── TokenStateK.kt
│ │ │ └── TokenStateUtilitiesK.kt
│ │ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ ├── contracts
│ │ │ ├── TokenContractIssueTests.java
│ │ │ ├── TokenContractMoveTests.java
│ │ │ └── TokenContractRedeemTests.java
│ │ │ └── states
│ │ │ ├── TokenStateTests.java
│ │ │ └── TokenStateUtilitiesMapSumByIssuerTests.java
│ │ └── kotlin
│ │ └── com
│ │ └── template
│ │ ├── contracts
│ │ ├── TokenContractIssueTestsK.kt
│ │ ├── TokenContractMoveTestsK.kt
│ │ └── TokenContractRedeemTestsK.kt
│ │ └── states
│ │ ├── TokenStateTestsK.kt
│ │ └── TokenStateUtilitiesMapSumByIssuerTestsK.kt
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── flows
│ │ │ ├── Constants.java
│ │ │ ├── IssueFlows.java
│ │ │ ├── MoveFlows.java
│ │ │ └── RedeemFlows.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── flows
│ │ ├── IssueFlowsK.kt
│ │ ├── MoveFlowsK.kt
│ │ └── RedeemFlowsK.kt
│ └── test
│ ├── java
│ └── com
│ │ └── template
│ │ ├── NodeDriver.java
│ │ └── flows
│ │ ├── FlowHelpers.java
│ │ ├── IssueFlowsTests.java
│ │ ├── MoveFlowsTests.java
│ │ └── RedeemFlowsTests.java
│ └── kotlin
│ └── com
│ └── template
│ ├── NodeDriverK.kt
│ └── flows
│ ├── FlowHelpersK.kt
│ ├── IssueFlowsTestsK.kt
│ ├── MoveFlowsTestsK.kt
│ └── RedeemFlowsTestsK.kt
├── 030-tokens-sdk
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ └── template
│ │ │ ├── car
│ │ │ ├── CarTokenContract.java
│ │ │ └── CarTokenType.java
│ │ │ ├── contracts
│ │ │ └── DummyContract.java
│ │ │ └── states
│ │ │ ├── AirMileType.java
│ │ │ └── TrialToken.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── template
│ │ ├── contracts
│ │ ├── TokenContractIssueTests.java
│ │ ├── TokenContractMoveTests.java
│ │ ├── TokenContractRedeemTests.java
│ │ └── TokenContractTestHelpers.java
│ │ └── states
│ │ └── AirMileTypeTests.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── AtomicSale.java
│ │ │ ├── CarTokenTypeConstants.java
│ │ │ ├── IssueCarToHolderFlow.java
│ │ │ ├── IssueCarTokenTypeFlow.java
│ │ │ ├── MoveCarToNewHolderFlow.java
│ │ │ └── UpdateCarTokenTypeFlow.java
│ │ ├── flows
│ │ ├── IssueFlows.java
│ │ ├── MoveFlows.java
│ │ └── RedeemFlows.java
│ │ └── usd
│ │ ├── IssueUsdFlow.java
│ │ ├── MoveUsdFlow.java
│ │ ├── RedeemUsdFlow.java
│ │ └── UsdTokenConstants.java
│ └── test
│ └── java
│ └── com
│ └── template
│ ├── NodeDriver.java
│ ├── car
│ └── flow
│ │ ├── AtomicSaleTests.java
│ │ ├── CarTokenCourseExercise.java
│ │ ├── CarTokenCourseHelpers.java
│ │ └── bad
│ │ └── NonAtomicSale.java
│ ├── flows
│ ├── FlowTestHelpers.java
│ ├── IssueFlowsTests.java
│ ├── MoveFlowsTests.java
│ └── RedeemFlowsTests.java
│ └── usd
│ ├── CurrencyLearningTest.java
│ ├── UsdTokenCourseExercise.java
│ └── UsdTokenCourseHelpers.java
├── 040-accounts-lib
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── template
│ │ ├── car
│ │ └── state
│ │ │ ├── CarTokenContract.java
│ │ │ └── CarTokenType.java
│ │ └── dummy
│ │ └── state
│ │ ├── DummyContract.java
│ │ └── DummyState.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── AtomicSale.java
│ │ │ ├── AtomicSaleAccounts.java
│ │ │ ├── AtomicSaleAccountsSafe.java
│ │ │ ├── CarTokenTypeConstants.java
│ │ │ ├── IssueCarToHolderFlow.java
│ │ │ ├── IssueCarTokenTypeFlow.java
│ │ │ ├── MoveCarToNewHolderFlow.java
│ │ │ └── UpdateCarTokenTypeFlow.java
│ │ ├── dummy
│ │ └── flow
│ │ │ └── DummyFlow.java
│ │ └── usd
│ │ └── UsdTokenConstants.java
│ └── test
│ └── java
│ └── com
│ └── template
│ ├── NodeDriver.java
│ ├── account
│ ├── AccountCourseExercise.java
│ ├── AccountTokenCourseExercise.java
│ ├── AccountTokenCourseHelpers.java
│ └── AccountVaultCourseExercise.java
│ └── car
│ └── flow
│ ├── AtomicSaleAccountsSafeTests.java
│ ├── AtomicSaleAccountsTests.java
│ ├── AtomicSaleTests.java
│ └── CarTokenCourseHelpers.java
├── 050-ref-state
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ ├── example
│ │ │ ├── contract
│ │ │ │ ├── IOUContract.java
│ │ │ │ └── KYCContract.java
│ │ │ └── state
│ │ │ │ ├── IOUState.java
│ │ │ │ └── KYCState.java
│ │ │ └── template
│ │ │ ├── car
│ │ │ └── state
│ │ │ │ ├── CarTokenContract.java
│ │ │ │ └── CarTokenType.java
│ │ │ ├── dummy
│ │ │ └── state
│ │ │ │ ├── DummyContract.java
│ │ │ │ └── DummyState.java
│ │ │ └── proposal
│ │ │ └── state
│ │ │ ├── SalesProposal.java
│ │ │ └── SalesProposalContract.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── template
│ │ └── proposal
│ │ └── state
│ │ ├── SalesProposalContractAcceptTests.java
│ │ ├── SalesProposalContractOfferTests.java
│ │ ├── SalesProposalContractRejectTests.java
│ │ ├── SalesProposalContractTestHelpers.java
│ │ └── SalesProposalTests.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ ├── example
│ │ └── flow
│ │ │ └── ExampleFlow.java
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── IssueCarToHolderFlows.java
│ │ │ ├── IssueCarTokenTypeFlows.java
│ │ │ ├── MoveCarToNewHolderFlows.java
│ │ │ └── UpdateCarTokenTypeFlows.java
│ │ ├── proposal
│ │ └── flow
│ │ │ ├── SalesProposalAcceptFlows.java
│ │ │ ├── SalesProposalOfferFlows.java
│ │ │ └── SalesProposalRejectFlows.java
│ │ └── usd
│ │ └── IssueCurrencyFlows.java
│ └── test
│ └── java
│ └── com
│ └── template
│ ├── NodeDriver.java
│ ├── car
│ └── flow
│ │ ├── CarTokenCourseHelpers.java
│ │ ├── CarTokenTypeConstants.java
│ │ └── UsdTokenConstants.java
│ └── proposal
│ └── flow
│ ├── SalesProposalAcceptFlowsTests.java
│ ├── SalesProposalOfferFlowsTests.java
│ └── SalesProposalRejectFlowsTests.java
├── 060-time-window
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ ├── example
│ │ │ ├── contract
│ │ │ │ ├── IOUContract.java
│ │ │ │ └── KYCContract.java
│ │ │ └── state
│ │ │ │ ├── IOUState.java
│ │ │ │ └── KYCState.java
│ │ │ └── template
│ │ │ ├── car
│ │ │ └── state
│ │ │ │ ├── CarTokenContract.java
│ │ │ │ └── CarTokenType.java
│ │ │ ├── dummy
│ │ │ └── state
│ │ │ │ ├── DummyContract.java
│ │ │ │ └── DummyState.java
│ │ │ └── proposal
│ │ │ └── state
│ │ │ ├── SalesProposal.java
│ │ │ └── SalesProposalContract.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── template
│ │ └── proposal
│ │ └── state
│ │ ├── SalesProposalContractAcceptTests.java
│ │ ├── SalesProposalContractOfferTests.java
│ │ ├── SalesProposalContractRejectTests.java
│ │ ├── SalesProposalContractTestHelpers.java
│ │ └── SalesProposalTests.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ ├── example
│ │ └── flow
│ │ │ └── ExampleFlow.java
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── IssueCarToHolderFlows.java
│ │ │ ├── IssueCarTokenTypeFlows.java
│ │ │ ├── MoveCarToNewHolderFlows.java
│ │ │ └── UpdateCarTokenTypeFlows.java
│ │ ├── proposal
│ │ └── flow
│ │ │ ├── SalesProposalAcceptFlows.java
│ │ │ ├── SalesProposalOfferFlows.java
│ │ │ └── SalesProposalRejectFlows.java
│ │ └── usd
│ │ └── IssueCurrencyFlows.java
│ └── test
│ └── java
│ └── com
│ └── template
│ ├── NodeDriver.java
│ ├── car
│ └── flow
│ │ ├── CarTokenCourseHelpers.java
│ │ ├── CarTokenTypeConstants.java
│ │ └── UsdTokenConstants.java
│ └── proposal
│ └── flow
│ ├── SalesProposalAcceptFlowsTests.java
│ ├── SalesProposalOfferFlowsTests.java
│ └── SalesProposalRejectFlowsTests.java
├── 070-services
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ ├── example
│ │ │ ├── contract
│ │ │ │ ├── IOUContract.java
│ │ │ │ └── KYCContract.java
│ │ │ └── state
│ │ │ │ ├── IOUState.java
│ │ │ │ └── KYCState.java
│ │ │ └── template
│ │ │ ├── car
│ │ │ └── state
│ │ │ │ ├── CarTokenContract.java
│ │ │ │ └── CarTokenType.java
│ │ │ ├── dummy
│ │ │ └── state
│ │ │ │ ├── DummyContract.java
│ │ │ │ └── DummyState.java
│ │ │ └── proposal
│ │ │ └── state
│ │ │ ├── SalesProposal.java
│ │ │ └── SalesProposalContract.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── template
│ │ └── proposal
│ │ └── state
│ │ ├── SalesProposalContractAcceptTests.java
│ │ ├── SalesProposalContractOfferTests.java
│ │ ├── SalesProposalContractRejectTests.java
│ │ ├── SalesProposalContractTestHelpers.java
│ │ └── SalesProposalTests.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ ├── example
│ │ └── flow
│ │ │ └── ExampleFlow.java
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── IssueCarToHolderFlows.java
│ │ │ ├── IssueCarTokenTypeFlows.java
│ │ │ ├── MoveCarToNewHolderFlows.java
│ │ │ └── UpdateCarTokenTypeFlows.java
│ │ ├── proposal
│ │ ├── flow
│ │ │ ├── InformTokenBuyerFlows.java
│ │ │ ├── SalesProposalAcceptFlows.java
│ │ │ ├── SalesProposalOfferFlows.java
│ │ │ ├── SalesProposalRejectFlows.java
│ │ │ └── SalesProposalUtils.java
│ │ └── service
│ │ │ └── SalesProposalService.java
│ │ └── usd
│ │ └── IssueCurrencyFlows.java
│ └── test
│ └── java
│ └── com
│ └── template
│ ├── NodeDriver.java
│ ├── car
│ └── flow
│ │ ├── CarTokenCourseHelpers.java
│ │ ├── CarTokenTypeConstants.java
│ │ └── UsdTokenConstants.java
│ └── proposal
│ ├── flow
│ ├── InformTokenBuyerFlowsTests.java
│ ├── SalesProposalAcceptFlowsTests.java
│ ├── SalesProposalOfferFlowsTests.java
│ ├── SalesProposalRejectFlowsSchedulableTests.java
│ └── SalesProposalRejectFlowsTests.java
│ └── service
│ ├── SalesProposalServiceAndSchedulableTests.java
│ └── SalesProposalServiceTests.java
├── 080-oracle
├── .idea
│ ├── .gitignore
│ ├── codeStyles
│ │ └── codeStyleConfig.xml
│ ├── compiler.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── uiDesigner.xml
│ └── vcs.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── README.md
├── build.gradle
├── config
│ ├── dev
│ │ └── log4j2.xml
│ └── test
│ │ └── log4j2.xml
├── contracts
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── com
│ │ │ ├── example
│ │ │ ├── contract
│ │ │ │ ├── FxContract.java
│ │ │ │ ├── IOUContract.java
│ │ │ │ ├── KYCContract.java
│ │ │ │ └── TemperatureContract.java
│ │ │ ├── oracle
│ │ │ │ ├── FxOracleUtilities.java
│ │ │ │ ├── FxQuote.java
│ │ │ │ └── TemperatureOracleUtilities.java
│ │ │ └── state
│ │ │ │ ├── FxState.java
│ │ │ │ ├── IOUState.java
│ │ │ │ └── KYCState.java
│ │ │ └── template
│ │ │ ├── car
│ │ │ └── state
│ │ │ │ ├── CarTokenContract.java
│ │ │ │ └── CarTokenType.java
│ │ │ ├── diligence
│ │ │ └── state
│ │ │ │ ├── DiligenceOracleUtilities.java
│ │ │ │ ├── DueDiligence.java
│ │ │ │ └── DueDiligenceContract.java
│ │ │ ├── dummy
│ │ │ └── state
│ │ │ │ ├── DummyContract.java
│ │ │ │ └── DummyState.java
│ │ │ └── proposal
│ │ │ └── state
│ │ │ ├── SalesProposal.java
│ │ │ └── SalesProposalContract.java
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── template
│ │ ├── diligence
│ │ └── state
│ │ │ ├── DueDiligenceContractCertifyTests.java
│ │ │ ├── DueDiligenceContractDropTests.java
│ │ │ ├── DueDiligenceContractPrepareTests.java
│ │ │ └── DueDiligenceTests.java
│ │ └── proposal
│ │ └── state
│ │ ├── SalesProposalContractAcceptTests.java
│ │ ├── SalesProposalContractOfferTests.java
│ │ ├── SalesProposalContractRejectTests.java
│ │ ├── SalesProposalContractTestHelpers.java
│ │ └── SalesProposalTests.java
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── repositories.gradle
├── settings.gradle
└── workflows
│ ├── build.gradle
│ ├── res
│ └── tokens-workflows.conf
│ └── src
│ ├── integrationTest
│ ├── java
│ │ └── com
│ │ │ └── template
│ │ │ └── DriverBasedTest.java
│ └── kotlin
│ │ └── com
│ │ └── template
│ │ └── DriverBasedTestK.kt
│ ├── main
│ └── java
│ │ └── com
│ │ ├── example
│ │ ├── flow
│ │ │ ├── ExampleFlow.java
│ │ │ └── FxOracleFlows.java
│ │ └── oracle
│ │ │ ├── FxOracle.java
│ │ │ └── TemperatureOracle.java
│ │ └── template
│ │ ├── car
│ │ └── flow
│ │ │ ├── IssueCarToHolderFlows.java
│ │ │ ├── IssueCarTokenTypeFlows.java
│ │ │ ├── MoveCarToNewHolderFlows.java
│ │ │ └── UpdateCarTokenTypeFlows.java
│ │ ├── diligence
│ │ └── flow
│ │ │ ├── DiligenceOracle.java
│ │ │ ├── DiligenceOracleInternalFlows.java
│ │ │ ├── DueDiligenceFlowUtils.java
│ │ │ └── DueDiligenceOracleFlows.java
│ │ ├── proposal
│ │ ├── flow
│ │ │ ├── InformTokenBuyerFlows.java
│ │ │ ├── SalesProposalAcceptFlows.java
│ │ │ ├── SalesProposalOfferFlows.java
│ │ │ ├── SalesProposalRejectFlows.java
│ │ │ └── SalesProposalUtils.java
│ │ └── service
│ │ │ └── SalesProposalService.java
│ │ └── usd
│ │ └── IssueCurrencyFlows.java
│ └── test
│ └── java
│ └── com
│ ├── example
│ └── flow
│ │ └── LinearStateFlowsTests.java
│ └── template
│ ├── NodeDriver.java
│ ├── car
│ └── flow
│ │ ├── CarTokenCourseHelpers.java
│ │ ├── CarTokenTypeConstants.java
│ │ └── UsdTokenConstants.java
│ ├── diligence
│ └── flow
│ │ ├── DueDiligenceFlowsTests.java
│ │ └── DueDiligenceOracleFlowsTests.java
│ └── proposal
│ ├── flow
│ ├── InformTokenBuyerFlowsTests.java
│ ├── SalesProposalAcceptDueDiligenceFlowsTests.java
│ ├── SalesProposalAcceptFlowsTests.java
│ ├── SalesProposalOfferFlowsTests.java
│ ├── SalesProposalRejectFlowsSchedulableTests.java
│ └── SalesProposalRejectFlowsTests.java
│ └── service
│ ├── SalesProposalServiceAndSchedulableTests.java
│ └── SalesProposalServiceTests.java
├── README.md
└── constants.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | # Eclipse, ctags, Mac metadata, log files
2 | .classpath
3 | .project
4 | tags
5 | .DS_Store
6 | *.log
7 | *.log.gz
8 | *.orig
9 |
10 | .gradle
11 |
12 | # General build files
13 | **/build/*
14 | !docs/build/*
15 |
16 | lib/dokka.jar
17 |
18 | ### JetBrains template
19 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
20 |
21 | *.iml
22 |
23 | ## Directory-based project format:
24 | #.idea
25 |
26 | # if you remove the above rule, at least ignore the following:
27 |
28 | # Specific files to avoid churn
29 | .idea/*.xml
30 | .idea/copyright
31 | .idea/jsLibraryMappings.xml
32 |
33 | # User-specific stuff:
34 | .idea/tasks.xml
35 | .idea/dictionaries
36 |
37 | # Sensitive or high-churn files:
38 | .idea/dataSources.ids
39 | .idea/dataSources.xml
40 | .idea/sqlDataSources.xml
41 | .idea/dynamic.xml
42 | .idea/uiDesigner.xml
43 |
44 | # Gradle:
45 | .idea/libraries
46 |
47 | # Mongo Explorer plugin:
48 | .idea/mongoSettings.xml
49 |
50 | ## File-based project format:
51 | *.ipr
52 | *.iws
53 |
54 | ## Plugin-specific files:
55 |
56 | # IntelliJ
57 | out/
58 | /workflows/out/
59 | /contracts/out/
60 | clients/out/
61 | bin/
62 |
63 | # mpeltonen/sbt-idea plugin
64 | .idea_modules/
65 |
66 | # JIRA plugin
67 | atlassian-ide-plugin.xml
68 |
69 | # Crashlytics plugin (for Android Studio and IntelliJ)
70 | com_crashlytics_export_strings.xml
71 | crashlytics.properties
72 | crashlytics-build.properties
73 |
74 | # docs related
75 | docs/virtualenv/
76 |
77 | # if you use the installQuasar task
78 | lib
79 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/010-empty-project/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/010-empty-project/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/010-empty-project/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/010-empty-project/README.md:
--------------------------------------------------------------------------------
1 | This represents the initial state, where we have prepared the ground but have not coded anything interesting.
2 |
3 | You can use this step to start working.
4 |
--------------------------------------------------------------------------------
/010-empty-project/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/010-empty-project/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | }
15 |
16 | sourceSets {
17 | main{
18 | java {
19 | srcDir 'src/main/java'
20 | java.outputDir = file('bin/main')
21 | }
22 | }
23 | test{
24 | java{
25 | srcDir 'src/test/java'
26 | java.outputDir = file('bin/test')
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
33 |
34 | // Corda dependencies.
35 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
36 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
37 | testCompile "junit:junit:$junit_version"
38 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
39 | }
40 |
41 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
42 | kotlinOptions {
43 | languageVersion = "1.1"
44 | apiVersion = "1.1"
45 | jvmTarget = "1.8"
46 | javaParameters = true // Useful for reflection.
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/main/java/com/template/contracts/TemplateContract.java:
--------------------------------------------------------------------------------
1 | package com.template.contracts;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | // ************
9 | // * Contract *
10 | // ************
11 | public class TemplateContract implements Contract {
12 | // This is used to identify our contract when building a transaction.
13 | public static final String ID = "com.template.contracts.TemplateContract";
14 |
15 | // A transaction is valid if the verify() function of the contract of all the transaction's input and output states
16 | // does not throw an exception.
17 | @Override
18 | public void verify(@NotNull LedgerTransaction tx) {}
19 |
20 | // Used to indicate the transaction's intent.
21 | public interface Commands extends CommandData {
22 | class Action implements Commands {}
23 | }
24 | }
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/main/java/com/template/states/TemplateState.java:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import com.template.contracts.TemplateContract;
4 | import net.corda.core.contracts.BelongsToContract;
5 | import net.corda.core.contracts.ContractState;
6 | import net.corda.core.identity.AbstractParty;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.util.Arrays;
10 | import java.util.List;
11 |
12 | // *********
13 | // * State *
14 | // *********
15 | @BelongsToContract(TemplateContract.class)
16 | public class TemplateState implements ContractState {
17 |
18 | public TemplateState() {
19 |
20 | }
21 |
22 | @NotNull
23 | @Override
24 | public List getParticipants() {
25 | return Arrays.asList();
26 | }
27 | }
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/main/kotlin/com/template/contracts/ExampleContract.kt:
--------------------------------------------------------------------------------
1 | package com.template.contracts
2 |
3 | import net.corda.core.contracts.CommandData
4 | import net.corda.core.contracts.Contract
5 | import net.corda.core.contracts.ContractState
6 | import net.corda.core.identity.AbstractParty
7 | import net.corda.core.transactions.LedgerTransaction
8 |
9 | // ************
10 | // * Contract *
11 | // ************
12 | class ExampleContract : Contract {
13 | companion object {
14 | // Used to identify our contract when building a transaction.
15 | const val ID = "com.template.contracts.ExampleContract"
16 | }
17 |
18 | // A transaction is valid if the verify() function of the contract of all the transaction's input and output states
19 | // does not throw an exception.
20 | override fun verify(tx: LedgerTransaction) {
21 | // Verification logic goes here.
22 | }
23 |
24 | // Used to indicate the transaction's intent.
25 | interface Commands : CommandData {
26 | class Action : Commands
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/main/kotlin/com/template/states/ExampleState.kt:
--------------------------------------------------------------------------------
1 | package com.template.states
2 |
3 | import net.corda.core.contracts.ContractState
4 | import net.corda.core.identity.AbstractParty
5 |
6 | // *********
7 | // * State *
8 | // *********
9 | data class ExampleState(val data: String) : ContractState {
10 | override val participants: List = listOf()
11 | }
12 |
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/test/java/com/template/contracts/ContractTests.java:
--------------------------------------------------------------------------------
1 | package com.template.contracts;
2 |
3 | import net.corda.testing.node.MockServices;
4 | import org.junit.Test;
5 |
6 | public class ContractTests {
7 | private final MockServices ledgerServices = new MockServices();
8 |
9 | @Test
10 | public void dummyTest() {
11 |
12 | }
13 | }
--------------------------------------------------------------------------------
/010-empty-project/contracts/src/test/kotlin/com/template/contracts/ContractTestsK.kt:
--------------------------------------------------------------------------------
1 | package com.template.contracts
2 |
3 | import net.corda.testing.node.MockServices
4 | import org.junit.Test
5 |
6 | class ContractTestsK {
7 | private val ledgerServices = MockServices()
8 |
9 | @Test
10 | fun `dummy test`() {
11 |
12 | }
13 | }
--------------------------------------------------------------------------------
/010-empty-project/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/010-empty-project/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/010-empty-project/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/010-empty-project/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/010-empty-project/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/010-empty-project/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/main/java/com/template/flows/Initiator.java:
--------------------------------------------------------------------------------
1 | package com.template.flows;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import net.corda.core.flows.FlowException;
5 | import net.corda.core.flows.FlowLogic;
6 | import net.corda.core.flows.InitiatingFlow;
7 | import net.corda.core.flows.StartableByRPC;
8 | import net.corda.core.utilities.ProgressTracker;
9 |
10 | // ******************
11 | // * Initiator flow *
12 | // ******************
13 | @InitiatingFlow
14 | @StartableByRPC
15 | public class Initiator extends FlowLogic {
16 | private final ProgressTracker progressTracker = new ProgressTracker();
17 |
18 | @Override
19 | public ProgressTracker getProgressTracker() {
20 | return progressTracker;
21 | }
22 |
23 | @Suspendable
24 | @Override
25 | public Void call() throws FlowException {
26 | // Initiator flow logic goes here.
27 |
28 | return null;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/main/java/com/template/flows/Responder.java:
--------------------------------------------------------------------------------
1 | package com.template.flows;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import net.corda.core.flows.FlowException;
5 | import net.corda.core.flows.FlowLogic;
6 | import net.corda.core.flows.FlowSession;
7 | import net.corda.core.flows.InitiatedBy;
8 |
9 | // ******************
10 | // * Responder flow *
11 | // ******************
12 | @InitiatedBy(Initiator.class)
13 | public class Responder extends FlowLogic {
14 | private FlowSession counterpartySession;
15 |
16 | public Responder(FlowSession counterpartySession) {
17 | this.counterpartySession = counterpartySession;
18 | }
19 |
20 | @Suspendable
21 | @Override
22 | public Void call() throws FlowException {
23 | // Responder flow logic goes here.
24 |
25 | return null;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/main/kotlin/com/template/Flows.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import co.paralleluniverse.fibers.Suspendable
4 | import net.corda.core.flows.*
5 | import net.corda.core.utilities.ProgressTracker
6 |
7 | // *********
8 | // * Flows *
9 | // *********
10 | @InitiatingFlow
11 | @StartableByRPC
12 | class Initiator : FlowLogic() {
13 | override val progressTracker = ProgressTracker()
14 |
15 | @Suspendable
16 | override fun call() {
17 | // Initiator flow logic goes here.
18 | }
19 | }
20 |
21 | @InitiatedBy(Initiator::class)
22 | class Responder(val counterpartySession: FlowSession) : FlowLogic() {
23 | @Suspendable
24 | override fun call() {
25 | // Responder flow logic goes here.
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/test/java/com/template/FlowTests.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.template.flows.Responder;
5 | import net.corda.testing.node.MockNetwork;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.StartedMockNode;
8 | import net.corda.testing.node.TestCordapp;
9 | import org.junit.After;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | public class FlowTests {
14 | private final MockNetwork network = new MockNetwork(new MockNetworkParameters(ImmutableList.of(
15 | TestCordapp.findCordapp("com.template.contracts"),
16 | TestCordapp.findCordapp("com.template.flows")
17 | )));
18 | private final StartedMockNode a = network.createNode();
19 | private final StartedMockNode b = network.createNode();
20 |
21 | public FlowTests() {
22 | a.registerInitiatedFlow(Responder.class);
23 | b.registerInitiatedFlow(Responder.class);
24 | }
25 |
26 | @Before
27 | public void setup() {
28 | network.runNetwork();
29 | }
30 |
31 | @After
32 | public void tearDown() {
33 | network.stopNodes();
34 | }
35 |
36 | @Test
37 | public void dummyTest() {
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/test/kotlin/com/template/FlowTestsK.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import net.corda.testing.node.MockNetwork
4 | import org.junit.After
5 | import org.junit.Before
6 | import org.junit.Test
7 |
8 | class FlowTestsK {
9 | private val network = MockNetwork(listOf("com.template"))
10 | private val a = network.createNode()
11 | private val b = network.createNode()
12 |
13 | init {
14 | listOf(a, b).forEach {
15 | it.registerInitiatedFlow(Responder::class.java)
16 | }
17 | }
18 |
19 | @Before
20 | fun setup() = network.runNetwork()
21 |
22 | @After
23 | fun tearDown() = network.stopNodes()
24 |
25 | @Test
26 | fun `dummy test`() {
27 |
28 | }
29 | }
--------------------------------------------------------------------------------
/010-empty-project/workflows/src/test/kotlin/com/template/NodeDriverK.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import net.corda.core.identity.CordaX500Name
4 | import net.corda.core.utilities.getOrThrow
5 | import net.corda.testing.driver.DriverParameters
6 | import net.corda.testing.driver.driver
7 | import net.corda.testing.node.User
8 |
9 | /**
10 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
11 | * environment.
12 | */
13 | fun main(args: Array) {
14 | val rpcUsers = listOf(User("user1", "test", permissions = setOf("ALL")))
15 |
16 | driver(DriverParameters(startNodesInProcess = true, waitForAllNodesToFinish = true)) {
17 | startNode(providedName = CordaX500Name("PartyA", "London", "GB"), rpcUsers = rpcUsers).getOrThrow()
18 | startNode(providedName = CordaX500Name("PartyB", "New York", "US"), rpcUsers = rpcUsers).getOrThrow()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/020-first-token/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/020-first-token/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/020-first-token/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/020-first-token/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/020-first-token/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/020-first-token/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/020-first-token/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/020-first-token/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/020-first-token/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/020-first-token/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/020-first-token/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/020-first-token/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | }
15 |
16 | sourceSets {
17 | main{
18 | java {
19 | srcDir 'src/main/java'
20 | java.outputDir = file('bin/main')
21 | }
22 | }
23 | test{
24 | java{
25 | srcDir 'src/test/java'
26 | java.outputDir = file('bin/test')
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
33 |
34 | // Corda dependencies.
35 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
36 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
37 | testCompile "junit:junit:$junit_version"
38 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
39 | }
40 |
41 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
42 | kotlinOptions {
43 | languageVersion = "1.1"
44 | apiVersion = "1.1"
45 | jvmTarget = "1.8"
46 | javaParameters = true // Useful for reflection.
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/020-first-token/contracts/src/main/java/com/template/states/TokenStateUtilities.java:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 | import net.corda.core.identity.Party;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | import java.util.Collection;
8 | import java.util.Map;
9 | import java.util.stream.Collectors;
10 |
11 | public interface TokenStateUtilities {
12 |
13 | /**
14 | * @param states The states to tally.
15 | * @return The mapped sums of token quantities per issuer.
16 | */
17 | static Map mapSumByIssuer(@NotNull final Collection states) {
18 | // Thanks to the Stream, we are able to return our List in one go, instead of creating a modifiable Map
19 | // and then conditionally putting elements to it with for... put.
20 | return ImmutableMap.copyOf(states.stream()
21 | // Our tokens surely have repeated issuers, so we have more than 1 state per issuer. We still want to
22 | // file our tokens per issuer, so we are going to create a Map(issuer -> Sum of quantities).
23 | .collect(Collectors.toConcurrentMap(
24 | // This is how to get the Map key for a given token.
25 | TokenState::getIssuer,
26 | // This is how to create a brand new Map value for a given token. Here, the quantity alone.
27 | TokenState::getQuantity,
28 | // This is how to merge 2 Map values that would otherwise conflict on a given key. We simply
29 | // add the quantities. Plus, we want to fail hard in case of overflow.
30 | Math::addExact)));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/020-first-token/contracts/src/main/kotlin/com/template/states/TokenStateK.kt:
--------------------------------------------------------------------------------
1 | package com.template.states
2 |
3 | import com.template.contracts.TokenContractK
4 | import net.corda.core.contracts.BelongsToContract
5 | import net.corda.core.contracts.ContractState
6 | import net.corda.core.identity.AbstractParty
7 | import net.corda.core.identity.Party
8 |
9 | @BelongsToContract(TokenContractK::class)
10 | data class TokenStateK(val issuer: Party, val holder: Party, val quantity: Long) : ContractState {
11 |
12 | override val participants: List = listOf(holder)
13 | }
14 |
--------------------------------------------------------------------------------
/020-first-token/contracts/src/main/kotlin/com/template/states/TokenStateUtilitiesK.kt:
--------------------------------------------------------------------------------
1 | package com.template.states
2 |
3 | /**
4 | * @receiver The states to tally.
5 | * @return The mapped sums of token quantities per issuer.
6 | */
7 | fun Iterable.mapSumByIssuer() =
8 | /* Our tokens surely have repeated issuers, so we have more than 1 state per issuer. We still want to
9 | * file our tokens per issuer, so we are going to first create a Map(issuer -> list of quantities). */
10 | groupBy({ it.issuer }) { it.quantity }
11 | // Take the sum of quantities and replace in place.
12 | // Plus, we want to fail hard in case of overflow.
13 | .mapValues { it.value.reduce { sum, quantity -> Math.addExact(sum, quantity) } }
14 | // Make it immutable
15 | .toMap()
16 |
--------------------------------------------------------------------------------
/020-first-token/contracts/src/test/kotlin/com/template/states/TokenStateTestsK.kt:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import net.corda.core.identity.CordaX500Name
4 | import net.corda.testing.core.TestIdentity
5 | import org.junit.Assert
6 | import org.junit.Test
7 | import kotlin.test.assertEquals
8 |
9 | class TokenStateTestsK {
10 |
11 | private val alice = TestIdentity(CordaX500Name("Alice", "London", "GB")).party
12 | private val bob = TestIdentity(CordaX500Name("Bob", "London", "GB")).party
13 | private val carly = TestIdentity(CordaX500Name("Carly", "London", "GB")).party
14 |
15 | @Test
16 | fun `it accepts a 0 amount`() {
17 | val token = TokenStateK(alice, bob, 0)
18 | assertEquals(0, token.quantity)
19 | }
20 |
21 | @Test
22 | fun `it accepts a negative amount`() {
23 | val token = TokenStateK(alice, bob, -1)
24 | assertEquals(-1, token.quantity)
25 | }
26 |
27 | // These tests are not necessary as this is the same as testing the `data class` construct.
28 | @Test
29 | fun `equals and hashcode identifies identical instances`() {
30 | val token1 = TokenStateK(alice, bob, 2)
31 | val token2 = TokenStateK(alice, bob, 2)
32 |
33 | assertEquals(token1, token2)
34 | assertEquals(token1.hashCode(), token2.hashCode())
35 | }
36 |
37 | @Test
38 | fun `equals and hashcode differentiate by issuer`() {
39 | val token1 = TokenStateK(alice, bob, 2)
40 | val token2 = TokenStateK(carly, bob, 2)
41 | Assert.assertNotEquals(token1, token2)
42 | Assert.assertNotEquals(token1.hashCode().toLong(), token2.hashCode().toLong())
43 | }
44 |
45 | @Test
46 | fun `equals and hashcode differentiate by owner`() {
47 | val token1 = TokenStateK(alice, bob, 2)
48 | val token2 = TokenStateK(alice, carly, 2)
49 | Assert.assertNotEquals(token1, token2)
50 | Assert.assertNotEquals(token1.hashCode().toLong(), token2.hashCode().toLong())
51 | }
52 |
53 | @Test
54 | fun `equals and hashcode differentiate by amount`() {
55 | val token1 = TokenStateK(alice, bob, 2)
56 | val token2 = TokenStateK(alice, bob, 3)
57 | Assert.assertNotEquals(token1, token2)
58 | Assert.assertNotEquals(token1.hashCode().toLong(), token2.hashCode().toLong())
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/020-first-token/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/020-first-token/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/020-first-token/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/020-first-token/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/020-first-token/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/020-first-token/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/020-first-token/workflows/src/integrationTest/kotlin/com/template/DriverBasedTestK.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import net.corda.core.identity.CordaX500Name
4 | import net.corda.core.utilities.getOrThrow
5 | import net.corda.testing.core.TestIdentity
6 | import net.corda.testing.driver.DriverDSL
7 | import net.corda.testing.driver.DriverParameters
8 | import net.corda.testing.driver.NodeHandle
9 | import net.corda.testing.driver.driver
10 | import org.junit.Test
11 | import java.util.concurrent.Future
12 | import kotlin.test.assertEquals
13 |
14 | class DriverBasedTestK {
15 | private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB"))
16 | private val bankB = TestIdentity(CordaX500Name("BankB", "", "US"))
17 |
18 | @Test
19 | fun `node test`() = withDriver {
20 | // Start a pair of nodes and wait for them both to be ready.
21 | val (partyAHandle, partyBHandle) = startNodes(bankA, bankB)
22 |
23 | // From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
24 | // nodes have started and can communicate.
25 |
26 | // This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
27 | // and other important metrics to ensure that your CorDapp is working as intended.
28 | assertEquals(bankB.name, partyAHandle.resolveName(bankB.name))
29 | assertEquals(bankA.name, partyBHandle.resolveName(bankA.name))
30 | }
31 |
32 | // Runs a test inside the Driver DSL, which provides useful functions for starting nodes, etc.
33 | private fun withDriver(test: DriverDSL.() -> Unit) = driver(
34 | DriverParameters(isDebug = true, startNodesInProcess = true)
35 | ) { test() }
36 |
37 | // Makes an RPC call to retrieve another node's name from the network map.
38 | private fun NodeHandle.resolveName(name: CordaX500Name) = rpc.wellKnownPartyFromX500Name(name)!!.name
39 |
40 | // Resolves a list of futures to a list of the promised values.
41 | private fun List>.waitForAll(): List = map { it.getOrThrow() }
42 |
43 | // Starts multiple nodes simultaneously, then waits for them all to be ready.
44 | private fun DriverDSL.startNodes(vararg identities: TestIdentity) = identities
45 | .map { startNode(providedName = it.name) }
46 | .waitForAll()
47 | }
--------------------------------------------------------------------------------
/020-first-token/workflows/src/main/java/com/template/flows/Constants.java:
--------------------------------------------------------------------------------
1 | package com.template.flows;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface Constants {
6 | // Hard-coded for simplicity's sake. In general, a configuration file is preferred.
7 | String desiredNotaryName = "O=Notary, L=London, C=GB";
8 | CordaX500Name desiredNotary = CordaX500Name.parse(desiredNotaryName);
9 | }
10 |
--------------------------------------------------------------------------------
/020-first-token/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/020-first-token/workflows/src/test/kotlin/com/template/NodeDriverK.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import net.corda.core.identity.CordaX500Name
4 | import net.corda.core.utilities.getOrThrow
5 | import net.corda.testing.driver.DriverParameters
6 | import net.corda.testing.driver.driver
7 | import net.corda.testing.node.User
8 |
9 | /**
10 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
11 | * environment.
12 | */
13 | fun main(args: Array) {
14 | val rpcUsers = listOf(User("user1", "test", permissions = setOf("ALL")))
15 |
16 | driver(DriverParameters(startNodesInProcess = true, waitForAllNodesToFinish = true)) {
17 | startNode(providedName = CordaX500Name("PartyA", "London", "GB"), rpcUsers = rpcUsers).getOrThrow()
18 | startNode(providedName = CordaX500Name("PartyB", "New York", "US"), rpcUsers = rpcUsers).getOrThrow()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/020-first-token/workflows/src/test/kotlin/com/template/flows/FlowHelpersK.kt:
--------------------------------------------------------------------------------
1 | package com.template.flows
2 |
3 | import com.template.states.TokenStateK
4 | import net.corda.core.utilities.getOrThrow
5 | import net.corda.testing.core.singleIdentity
6 | import net.corda.testing.node.MockNetwork
7 | import net.corda.testing.node.StartedMockNode
8 | import kotlin.test.assertEquals
9 |
10 | fun createFrom(
11 | issuer: StartedMockNode,
12 | holder: StartedMockNode,
13 | quantity: Long) = TokenStateK(
14 | issuer.info.singleIdentity(),
15 | holder.info.singleIdentity(),
16 | quantity)
17 |
18 | fun TokenStateK.toPair() = Pair(holder, quantity)
19 |
20 | fun StartedMockNode.assertHasStatesInVault(tokenStates: List) {
21 | val vaultTokens = transaction {
22 | services.vaultService.queryBy(TokenStateK::class.java).states
23 | }
24 | assertEquals(tokenStates.size, vaultTokens.size)
25 | assertEquals(tokenStates, vaultTokens.map { it.state.data })
26 | }
27 |
28 | class NodeHolding(val holder: StartedMockNode, val quantity: Long) {
29 | fun toPair() = Pair(holder.info.singleIdentity(), quantity)
30 | }
31 |
32 | fun StartedMockNode.issueTokens(network: MockNetwork, nodeHoldings: Collection) =
33 | IssueFlowsK.Initiator(nodeHoldings.map(NodeHolding::toPair))
34 | .let { startFlow(it) }
35 | .also { network.runNetwork() }
36 | .getOrThrow()
37 | .toLedgerTransaction(services)
38 | .outRefsOfType()
39 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/030-tokens-sdk/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/030-tokens-sdk/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/030-tokens-sdk/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | }
50 |
51 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
52 | kotlinOptions {
53 | languageVersion = "1.1"
54 | apiVersion = "1.1"
55 | jvmTarget = "1.8"
56 | javaParameters = true // Useful for reflection.
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/src/main/java/com/template/contracts/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.contracts;
2 |
3 | import net.corda.core.contracts.Contract;
4 | import net.corda.core.transactions.LedgerTransaction;
5 | import org.apache.commons.lang3.NotImplementedException;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | /**
9 | * Corda has "a bug" that if a "Contracts" CorDapp doesn't contain a contract, it doesn't load the Cordapp
10 | * on node startup.
11 | */
12 | @SuppressWarnings("unused")
13 | public class DummyContract implements Contract {
14 | private DummyContract() {
15 | throw new NotImplementedException("This contract is not to be used");
16 | }
17 |
18 | @Override
19 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
20 | throw new NotImplementedException("This contract is not to be used");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/src/main/java/com/template/states/AirMileType.java:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
4 | import com.r3.corda.lib.tokens.contracts.utilities.TransactionUtilitiesKt;
5 | import net.corda.core.crypto.SecureHash;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Objects;
9 |
10 | public final class AirMileType extends TokenType {
11 |
12 | public static final String IDENTIFIER = "AIR";
13 | public static final int FRACTION_DIGITS = 0;
14 |
15 | @NotNull
16 | public static SecureHash getContractAttachment() {
17 | //noinspection ConstantConditions
18 | return TransactionUtilitiesKt.getAttachmentIdForGenericParam(new AirMileType());
19 | }
20 |
21 | /**
22 | * This creates the {@link TokenType} for air-miles. Beware of the JAR hash issues.
23 | */
24 | public AirMileType() {
25 | super(IDENTIFIER, FRACTION_DIGITS);
26 | }
27 |
28 | @Override
29 | public boolean equals(final Object o) {
30 | if (this == o) return true;
31 | return o != null && getClass() == o.getClass();
32 | }
33 |
34 | @Override
35 | public int hashCode() {
36 | return Objects.hash("AirMileType");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/src/main/java/com/template/states/TrialToken.java:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import net.corda.core.contracts.*;
4 | import net.corda.core.identity.AbstractParty;
5 | import org.apache.commons.lang3.NotImplementedException;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 |
11 | /**
12 | * For demonstration purposes only.
13 | */
14 | @SuppressWarnings("unused")
15 | public class TrialToken implements FungibleState>, OwnableState {
16 |
17 | @NotNull
18 | private final Amount> amount;
19 | @NotNull
20 | private final AbstractParty owner;
21 |
22 | public TrialToken(
23 | @NotNull final Amount> amount,
24 | @NotNull final AbstractParty owner) {
25 | this.amount = amount;
26 | this.owner = owner;
27 | }
28 |
29 | @NotNull
30 | @Override
31 | public Amount> getAmount() {
32 | return amount;
33 | }
34 |
35 | @NotNull
36 | @Override
37 | public List getParticipants() {
38 | return Collections.singletonList(owner);
39 | }
40 |
41 | @NotNull
42 | @Override
43 | public AbstractParty getOwner() {
44 | return owner;
45 | }
46 |
47 | @NotNull
48 | public AbstractParty getIssuer() {
49 | return amount.getToken().getIssuer().getParty();
50 | }
51 |
52 | @NotNull
53 | public T getProduct() {
54 | return amount.getToken().getProduct();
55 | }
56 |
57 | @NotNull
58 | @Override
59 | public CommandAndState withNewOwner(@NotNull final AbstractParty newOwner) {
60 | throw new NotImplementedException("We need a contract command");
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/src/test/java/com/template/contracts/TokenContractTestHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.contracts;
2 |
3 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
4 | import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
5 | import com.template.states.AirMileType;
6 | import net.corda.core.contracts.Amount;
7 | import net.corda.core.identity.Party;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public interface TokenContractTestHelpers {
11 | @NotNull
12 | static FungibleToken create(
13 | @NotNull final IssuedTokenType tokenType,
14 | @NotNull final Party holder,
15 | final long quantity) {
16 | return new FungibleToken(new Amount<>(quantity, tokenType), holder, AirMileType.getContractAttachment());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/030-tokens-sdk/contracts/src/test/java/com/template/states/AirMileTypeTests.java:
--------------------------------------------------------------------------------
1 | package com.template.states;
2 |
3 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertEquals;
7 | import static org.junit.Assert.assertNotEquals;
8 |
9 | public class AirMileTypeTests {
10 |
11 | @Test
12 | public void hashCodeIsConstant() {
13 | assertEquals(new AirMileType().hashCode(), new AirMileType().hashCode());
14 | }
15 |
16 | @Test
17 | public void equalsIsOkWithSame() {
18 | assertEquals(new AirMileType(), new AirMileType());
19 | }
20 |
21 | @Test
22 | public void equalsIsDifferentWithNull() {
23 | assertNotEquals(new AirMileType(), null);
24 | }
25 |
26 | @Test
27 | public void equalsIsDifferentWithOtherTokenType() {
28 | assertNotEquals(new AirMileType(), new TokenType(AirMileType.IDENTIFIER, AirMileType.FRACTION_DIGITS));
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/030-tokens-sdk/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/030-tokens-sdk/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/030-tokens-sdk/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/030-tokens-sdk/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/030-tokens-sdk/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/030-tokens-sdk/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=New York, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/car/flow/IssueCarToHolderFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
5 | import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
6 | import com.r3.corda.lib.tokens.contracts.types.TokenPointer;
7 | import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens;
8 | import com.template.car.CarTokenType;
9 | import net.corda.core.contracts.UniqueIdentifier;
10 | import net.corda.core.flows.FlowException;
11 | import net.corda.core.flows.FlowLogic;
12 | import net.corda.core.identity.AbstractParty;
13 | import net.corda.core.identity.Party;
14 | import net.corda.core.transactions.SignedTransaction;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.Collections;
18 |
19 | public class IssueCarToHolderFlow extends FlowLogic {
20 |
21 | @NotNull
22 | private final CarTokenType car;
23 | @NotNull
24 | private final Party dealership;
25 | @NotNull
26 | private final AbstractParty holder;
27 |
28 | public IssueCarToHolderFlow(
29 | @NotNull final CarTokenType car, @NotNull final Party dealership, @NotNull final AbstractParty holder) {
30 | this.car = car;
31 | this.dealership = dealership;
32 | this.holder = holder;
33 | }
34 |
35 | @Suspendable
36 | @Override
37 | public SignedTransaction call() throws FlowException {
38 | final TokenPointer bmwPointer = car.toPointer(CarTokenType.class);
39 | final IssuedTokenType bmwWithDealership = new IssuedTokenType(dealership, bmwPointer);
40 | final NonFungibleToken heldCar = new NonFungibleToken(
41 | bmwWithDealership, holder,
42 | new UniqueIdentifier(), null);
43 | return subFlow(new IssueTokens(Collections.singletonList(heldCar)));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/car/flow/IssueCarTokenTypeFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.workflows.flows.rpc.CreateEvolvableTokens;
5 | import com.template.car.CarTokenType;
6 | import net.corda.core.contracts.TransactionState;
7 | import net.corda.core.contracts.UniqueIdentifier;
8 | import net.corda.core.flows.FlowException;
9 | import net.corda.core.flows.FlowLogic;
10 | import net.corda.core.identity.Party;
11 | import net.corda.core.transactions.SignedTransaction;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | import java.util.Collections;
15 | import java.util.List;
16 |
17 | public class IssueCarTokenTypeFlow extends FlowLogic {
18 |
19 | @NotNull
20 | private final Party notary;
21 | @NotNull
22 | private final String vin;
23 | @NotNull
24 | private final String make;
25 | private final long price;
26 | @NotNull
27 | private final List observers;
28 |
29 | public IssueCarTokenTypeFlow(
30 | @NotNull final Party notary, @NotNull final String vin, @NotNull final String make,
31 | final long price, @NotNull List observers) {
32 | this.notary = notary;
33 | this.vin = vin;
34 | this.make = make;
35 | this.price = price;
36 | this.observers = observers;
37 | }
38 |
39 | @Suspendable
40 | @Override
41 | public SignedTransaction call() throws FlowException {
42 | final Party dmv = getOurIdentity();
43 | if (!dmv.getName().equals(CarTokenTypeConstants.DMV)) {
44 | throw new FlowException("We are not the DMV");
45 | }
46 | final CarTokenType newCar = new CarTokenType(Collections.singletonList(dmv),
47 | new UniqueIdentifier(), vin, make, 0, price);
48 | final TransactionState txState = new TransactionState<>(newCar, notary);
49 | return subFlow(new CreateEvolvableTokens(txState, observers));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/car/flow/MoveCarToNewHolderFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.types.TokenPointer;
5 | import com.r3.corda.lib.tokens.workflows.flows.rpc.MoveNonFungibleTokens;
6 | import com.r3.corda.lib.tokens.workflows.types.PartyAndToken;
7 | import com.template.car.CarTokenType;
8 | import net.corda.core.flows.FlowException;
9 | import net.corda.core.flows.FlowLogic;
10 | import net.corda.core.identity.AbstractParty;
11 | import net.corda.core.identity.Party;
12 | import net.corda.core.transactions.SignedTransaction;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import java.util.List;
16 |
17 | public class MoveCarToNewHolderFlow extends FlowLogic {
18 |
19 | @NotNull
20 | private final TokenPointer carTokenTypePointer;
21 | @NotNull
22 | private final AbstractParty newHolder;
23 | @NotNull
24 | private final List observers;
25 |
26 | public MoveCarToNewHolderFlow(
27 | @NotNull TokenPointer carTokenTypePointer,
28 | @NotNull AbstractParty newHolder,
29 | @NotNull List observers) {
30 | this.carTokenTypePointer = carTokenTypePointer;
31 | this.newHolder = newHolder;
32 | this.observers = observers;
33 | }
34 |
35 | @Suspendable
36 | @Override
37 | public SignedTransaction call() throws FlowException {
38 | final PartyAndToken newHolderAndCar = new PartyAndToken(newHolder, carTokenTypePointer);
39 | return subFlow(new MoveNonFungibleTokens(newHolderAndCar, observers));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/car/flow/UpdateCarTokenTypeFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.workflows.flows.rpc.UpdateEvolvableToken;
5 | import com.template.car.CarTokenType;
6 | import net.corda.core.contracts.StateAndRef;
7 | import net.corda.core.flows.FlowException;
8 | import net.corda.core.flows.FlowLogic;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.SignedTransaction;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | import java.util.List;
14 |
15 | public class UpdateCarTokenTypeFlow extends FlowLogic {
16 |
17 | @NotNull
18 | private final StateAndRef carRef;
19 | private final long mileage;
20 | private final long price;
21 | @NotNull
22 | private final List observers;
23 |
24 | public UpdateCarTokenTypeFlow(
25 | @NotNull final StateAndRef carRef,
26 | final long mileage, final long price,
27 | final @NotNull List observers) {
28 | this.carRef = carRef;
29 | this.mileage = mileage;
30 | this.price = price;
31 | this.observers = observers;
32 | }
33 |
34 | @Suspendable
35 | @Override
36 | public SignedTransaction call() throws FlowException {
37 | final CarTokenType car = carRef.getState().getData();
38 | final CarTokenType updatedCar = new CarTokenType(car.getMaintainers(), car.getLinearId(),
39 | car.getVin(), car.getMake(), mileage, price);
40 | return subFlow(new UpdateEvolvableToken(carRef, updatedCar, observers));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/usd/IssueUsdFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
5 | import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
6 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
7 | import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilitiesKt;
8 | import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens;
9 | import net.corda.core.contracts.Amount;
10 | import net.corda.core.flows.FlowException;
11 | import net.corda.core.flows.FlowLogic;
12 | import net.corda.core.identity.Party;
13 | import net.corda.core.transactions.SignedTransaction;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.Collections;
17 |
18 | class IssueUsdFlow extends FlowLogic {
19 | @NotNull
20 | private final Party alice;
21 | private final long amount;
22 |
23 | public IssueUsdFlow(@NotNull final Party alice, final long amount) {
24 | this.alice = alice;
25 | this.amount = amount;
26 | }
27 |
28 | @Override
29 | @Suspendable
30 | public SignedTransaction call() throws FlowException {
31 | final TokenType usdTokenType = new TokenType("USD", 2);
32 | if (!getOurIdentity().getName().equals(UsdTokenConstants.US_MINT)) {
33 | throw new FlowException("We are not the US Mint");
34 | }
35 | final IssuedTokenType usMintUsd = new IssuedTokenType(getOurIdentity(), usdTokenType);
36 |
37 | // Who is going to own the output, and how much?
38 | // Create a 100$ token that can be split and merged.
39 | final Amount amountOfUsd = AmountUtilitiesKt.amount(amount, usMintUsd);
40 | final FungibleToken usdToken = new FungibleToken(amountOfUsd, alice, null);
41 |
42 | // Issue the token to alice.
43 | return subFlow(new IssueTokens(
44 | Collections.singletonList(usdToken), // Output instances
45 | Collections.emptyList())); // Observers
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/usd/RedeemUsdFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
5 | import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilitiesKt;
6 | import com.r3.corda.lib.tokens.money.FiatCurrency;
7 | import com.r3.corda.lib.tokens.workflows.flows.rpc.RedeemFungibleTokens;
8 | import com.r3.corda.lib.tokens.workflows.utilities.QueryUtilitiesKt;
9 | import net.corda.core.contracts.Amount;
10 | import net.corda.core.flows.FlowException;
11 | import net.corda.core.flows.FlowLogic;
12 | import net.corda.core.identity.Party;
13 | import net.corda.core.node.services.vault.QueryCriteria;
14 | import net.corda.core.transactions.SignedTransaction;
15 |
16 | import java.util.Collections;
17 |
18 | class RedeemUsdFlow extends FlowLogic {
19 | private final long amount;
20 |
21 | RedeemUsdFlow(final long amount) {
22 | this.amount = amount;
23 | }
24 |
25 | @Override
26 | @Suspendable
27 | public SignedTransaction call() throws FlowException {
28 | final TokenType usdTokenType = FiatCurrency.Companion.getInstance("USD");
29 | final Party usMint = getServiceHub().getNetworkMapCache().getPeerByLegalName(UsdTokenConstants.US_MINT);
30 | if (usMint == null) throw new FlowException("No US Mint found");
31 |
32 | // Describe how to find those $ held by Me.
33 | final QueryCriteria heldByMe = QueryUtilitiesKt.heldTokenAmountCriteria(usdTokenType, getOurIdentity());
34 | final Amount usdAmount = AmountUtilitiesKt.amount(amount, usdTokenType);
35 |
36 | // Do the redeem
37 | return subFlow(new RedeemFungibleTokens(
38 | usdAmount, // How much to redeem
39 | usMint, // issuer
40 | Collections.emptyList(), // Observers
41 | heldByMe, // Criteria to find the inputs
42 | getOurIdentity())); // change holder
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/main/java/com/template/usd/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.template.states"),
23 | TestCordapp.findCordapp("com.template.flows")))
24 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
25 | Collections.emptyList(), 4
26 | ));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/test/java/com/template/usd/CurrencyLearningTest.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
4 | import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilitiesKt;
5 | import com.r3.corda.lib.tokens.money.DigitalCurrency;
6 | import net.corda.core.contracts.Amount;
7 | import org.junit.Test;
8 |
9 | import static org.junit.Assert.assertEquals;
10 |
11 | public class CurrencyLearningTest {
12 |
13 | public CurrencyLearningTest() {
14 | }
15 |
16 | @Test(expected = ArithmeticException.class)
17 | public void cannotRepresentTenEth() {
18 | final TokenType etherType = DigitalCurrency.Companion.getInstance("ETH");
19 | final Amount oneEther = AmountUtilitiesKt.amount(1, etherType);
20 |
21 | // One Ether in Wei is 19 digits long.
22 | assertEquals(19, String.valueOf(oneEther.getQuantity()).length());
23 | // 19 is also the maximum number of digits that a "long" can hold!
24 | assertEquals(19, String.valueOf(Long.MAX_VALUE).length());
25 |
26 | // Now let's try to create 10 Ether,
27 | // which in Wei is 20 digits long and cannot be represented by "long" type.
28 | AmountUtilitiesKt.amount(10, etherType);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/030-tokens-sdk/workflows/src/test/java/com/template/usd/UsdTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
5 | import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
6 | import com.r3.corda.lib.tokens.contracts.types.TokenType;
7 | import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilitiesKt;
8 | import net.corda.core.contracts.Amount;
9 | import net.corda.testing.node.MockNetworkParameters;
10 | import net.corda.testing.node.StartedMockNode;
11 | import net.corda.testing.node.TestCordapp;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | public interface UsdTokenCourseHelpers {
15 | @NotNull
16 | static MockNetworkParameters prepareMockNetworkParameters() {
17 | return new MockNetworkParameters()
18 | .withCordappsForAllNodes(ImmutableList.of(
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
23 | TestCordapp.findCordapp("com.template.states"),
24 | TestCordapp.findCordapp("com.template.flows")));
25 | }
26 |
27 | @NotNull
28 | static FungibleToken createUsdFungible(
29 | @NotNull final StartedMockNode issuer,
30 | @NotNull final StartedMockNode holder,
31 | final long quantity) {
32 | final TokenType usdType = new TokenType("USD", 2);
33 | final IssuedTokenType issued = new IssuedTokenType(issuer.getInfo().getLegalIdentities().get(0), usdType);
34 | final Amount amount = AmountUtilitiesKt.amount(quantity, issued);
35 | return new FungibleToken(amount, holder.getInfo().getLegalIdentities().get(0), null);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/040-accounts-lib/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/040-accounts-lib/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/040-accounts-lib/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/040-accounts-lib/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/040-accounts-lib/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | cordapp "$accounts_release_group:accounts-contracts:$accounts_release_version"
50 | }
51 |
52 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
53 | kotlinOptions {
54 | languageVersion = "1.1"
55 | apiVersion = "1.1"
56 | jvmTarget = "1.8"
57 | javaParameters = true // Useful for reflection.
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/040-accounts-lib/contracts/src/main/java/com/template/dummy/state/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class DummyContract implements Contract {
9 | @Override
10 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
11 | // Do nothing
12 | }
13 |
14 | public interface Commands extends CommandData {
15 | public class Create implements Commands {
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/040-accounts-lib/contracts/src/main/java/com/template/dummy/state/DummyState.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.BelongsToContract;
4 | import net.corda.core.contracts.ContractState;
5 | import net.corda.core.identity.AbstractParty;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(DummyContract.class)
13 | public class DummyState implements ContractState {
14 | @NotNull
15 | private final AbstractParty lost;
16 | @NotNull
17 | private final AbstractParty seen;
18 |
19 | public DummyState(@NotNull final AbstractParty lost, @NotNull final AbstractParty seen) {
20 | this.lost = lost;
21 | this.seen = seen;
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public List getParticipants() {
27 | return Collections.singletonList(seen);
28 | }
29 |
30 | @NotNull
31 | public AbstractParty getLost() {
32 | return lost;
33 | }
34 |
35 | @NotNull
36 | public AbstractParty getSeen() {
37 | return seen;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object o) {
42 | if (this == o) return true;
43 | if (o == null || getClass() != o.getClass()) return false;
44 | final DummyState that = (DummyState) o;
45 | return lost.equals(that.lost) &&
46 | seen.equals(that.seen);
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | return Objects.hash(lost, seen);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/040-accounts-lib/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/040-accounts-lib/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/040-accounts-lib/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/040-accounts-lib/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/040-accounts-lib/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/040-accounts-lib/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=New York, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/car/flow/IssueCarToHolderFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
5 | import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
6 | import com.r3.corda.lib.tokens.contracts.types.TokenPointer;
7 | import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens;
8 | import com.template.car.state.CarTokenType;
9 | import net.corda.core.contracts.UniqueIdentifier;
10 | import net.corda.core.flows.FlowException;
11 | import net.corda.core.flows.FlowLogic;
12 | import net.corda.core.identity.AbstractParty;
13 | import net.corda.core.identity.Party;
14 | import net.corda.core.transactions.SignedTransaction;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.Collections;
18 |
19 | public class IssueCarToHolderFlow extends FlowLogic {
20 |
21 | @NotNull
22 | private final CarTokenType car;
23 | @NotNull
24 | private final Party dealership;
25 | @NotNull
26 | private final AbstractParty holder;
27 |
28 | public IssueCarToHolderFlow(
29 | @NotNull final CarTokenType car, @NotNull final Party dealership, @NotNull final AbstractParty holder) {
30 | this.car = car;
31 | this.dealership = dealership;
32 | this.holder = holder;
33 | }
34 |
35 | @Suspendable
36 | @Override
37 | public SignedTransaction call() throws FlowException {
38 | final TokenPointer bmwPointer = car.toPointer(CarTokenType.class);
39 | final IssuedTokenType bmwWithDealership = new IssuedTokenType(dealership, bmwPointer);
40 | final NonFungibleToken heldCar = new NonFungibleToken(
41 | bmwWithDealership, holder,
42 | new UniqueIdentifier(), null);
43 | return subFlow(new IssueTokens(Collections.singletonList(heldCar)));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/car/flow/IssueCarTokenTypeFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.workflows.flows.rpc.CreateEvolvableTokens;
5 | import com.template.car.state.CarTokenType;
6 | import net.corda.core.contracts.TransactionState;
7 | import net.corda.core.contracts.UniqueIdentifier;
8 | import net.corda.core.flows.FlowException;
9 | import net.corda.core.flows.FlowLogic;
10 | import net.corda.core.identity.Party;
11 | import net.corda.core.transactions.SignedTransaction;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | import java.util.Collections;
15 | import java.util.List;
16 |
17 | public class IssueCarTokenTypeFlow extends FlowLogic {
18 |
19 | @NotNull
20 | private final Party notary;
21 | @NotNull
22 | private final String vin;
23 | @NotNull
24 | private final String make;
25 | private final long price;
26 | @NotNull
27 | private final List observers;
28 |
29 | public IssueCarTokenTypeFlow(
30 | @NotNull final Party notary, @NotNull final String vin, @NotNull final String make,
31 | final long price, @NotNull List observers) {
32 | this.notary = notary;
33 | this.vin = vin;
34 | this.make = make;
35 | this.price = price;
36 | this.observers = observers;
37 | }
38 |
39 | @Suspendable
40 | @Override
41 | public SignedTransaction call() throws FlowException {
42 | final Party dmv = getOurIdentity();
43 | if (!dmv.getName().equals(CarTokenTypeConstants.DMV)) {
44 | throw new FlowException("We are not the DMV");
45 | }
46 | final CarTokenType newCar = new CarTokenType(Collections.singletonList(dmv),
47 | new UniqueIdentifier(), vin, make, 0, price);
48 | final TransactionState txState = new TransactionState<>(newCar, notary);
49 | return subFlow(new CreateEvolvableTokens(txState, observers));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/car/flow/MoveCarToNewHolderFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.contracts.types.TokenPointer;
5 | import com.r3.corda.lib.tokens.workflows.flows.rpc.MoveNonFungibleTokens;
6 | import com.r3.corda.lib.tokens.workflows.types.PartyAndToken;
7 | import com.template.car.state.CarTokenType;
8 | import net.corda.core.flows.FlowException;
9 | import net.corda.core.flows.FlowLogic;
10 | import net.corda.core.identity.AbstractParty;
11 | import net.corda.core.identity.Party;
12 | import net.corda.core.transactions.SignedTransaction;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import java.util.List;
16 |
17 | public class MoveCarToNewHolderFlow extends FlowLogic {
18 |
19 | @NotNull
20 | private final TokenPointer carTokenTypePointer;
21 | @NotNull
22 | private final AbstractParty newHolder;
23 | @NotNull
24 | private final List observers;
25 |
26 | public MoveCarToNewHolderFlow(
27 | @NotNull TokenPointer carTokenTypePointer,
28 | @NotNull AbstractParty newHolder,
29 | @NotNull List observers) {
30 | this.carTokenTypePointer = carTokenTypePointer;
31 | this.newHolder = newHolder;
32 | this.observers = observers;
33 | }
34 |
35 | @Suspendable
36 | @Override
37 | public SignedTransaction call() throws FlowException {
38 | final PartyAndToken newHolderAndCar = new PartyAndToken(newHolder, carTokenTypePointer);
39 | return subFlow(new MoveNonFungibleTokens(newHolderAndCar, observers));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/car/flow/UpdateCarTokenTypeFlow.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.r3.corda.lib.tokens.workflows.flows.rpc.UpdateEvolvableToken;
5 | import com.template.car.state.CarTokenType;
6 | import net.corda.core.contracts.StateAndRef;
7 | import net.corda.core.flows.FlowException;
8 | import net.corda.core.flows.FlowLogic;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.SignedTransaction;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | import java.util.List;
14 |
15 | public class UpdateCarTokenTypeFlow extends FlowLogic {
16 |
17 | @NotNull
18 | private final StateAndRef carRef;
19 | private final long mileage;
20 | private final long price;
21 | @NotNull
22 | private final List observers;
23 |
24 | public UpdateCarTokenTypeFlow(
25 | @NotNull final StateAndRef carRef,
26 | final long mileage, final long price,
27 | final @NotNull List observers) {
28 | this.carRef = carRef;
29 | this.mileage = mileage;
30 | this.price = price;
31 | this.observers = observers;
32 | }
33 |
34 | @Suspendable
35 | @Override
36 | public SignedTransaction call() throws FlowException {
37 | final CarTokenType car = carRef.getState().getData();
38 | final CarTokenType updatedCar = new CarTokenType(car.getMaintainers(), car.getLinearId(),
39 | car.getVin(), car.getMake(), mileage, price);
40 | return subFlow(new UpdateEvolvableToken(carRef, updatedCar, observers));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/main/java/com/template/usd/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.usd;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/test/java/com/template/account/AccountTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.account;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.template.car.flow.CarTokenTypeConstants;
5 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
6 | import net.corda.testing.node.MockNetworkNotarySpec;
7 | import net.corda.testing.node.MockNetworkParameters;
8 | import net.corda.testing.node.TestCordapp;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.util.Collections;
12 |
13 | public interface AccountTokenCourseHelpers {
14 | @NotNull
15 | static MockNetworkParameters prepareMockNetworkParameters() {
16 | return new MockNetworkParameters()
17 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
18 | .withCordappsForAllNodes(ImmutableList.of(
19 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
25 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
26 | TestCordapp.findCordapp("com.template.car.state"),
27 | TestCordapp.findCordapp("com.template.car.flow")))
28 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
29 | Collections.emptyList(), 4
30 | ));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/040-accounts-lib/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
25 | TestCordapp.findCordapp("com.template.car.state"),
26 | TestCordapp.findCordapp("com.template.car.flow")))
27 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
28 | Collections.emptyList(), 4
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/050-ref-state/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/050-ref-state/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/050-ref-state/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/050-ref-state/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/050-ref-state/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | cordapp "$accounts_release_group:accounts-contracts:$accounts_release_version"
50 | }
51 |
52 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
53 | kotlinOptions {
54 | languageVersion = "1.1"
55 | apiVersion = "1.1"
56 | jvmTarget = "1.8"
57 | javaParameters = true // Useful for reflection.
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/src/main/java/com/example/contract/KYCContract.java:
--------------------------------------------------------------------------------
1 | package com.example.contract;
2 |
3 | import com.example.state.KYCState;
4 | import net.corda.core.contracts.CommandData;
5 | import net.corda.core.contracts.CommandWithParties;
6 | import net.corda.core.contracts.Contract;
7 | import net.corda.core.transactions.LedgerTransaction;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.List;
11 |
12 | import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
13 | import static net.corda.core.contracts.ContractsDSL.requireThat;
14 |
15 | public class KYCContract implements Contract {
16 | @Override
17 | public void verify(@NotNull final LedgerTransaction tx) throws IllegalArgumentException {
18 | final CommandWithParties command = requireSingleCommand(tx.getCommands(), Commands.class);
19 | if (command.getValue() instanceof Commands.Create) {
20 | requireThat(require -> {
21 | // Generic constraints around the KYC transaction.
22 | require.using("No KYC inputs should be consumed when issuing a KYC.",
23 | tx.inputsOfType(KYCState.class).isEmpty());
24 | final List outList = tx.outputsOfType(KYCState.class);
25 | require.using("Only one output state should be created.",
26 | outList.size() == 1);
27 | final KYCState state = outList.get(0);
28 |
29 | // Signatures constraints.
30 | require.using("The issuer must be the signer.",
31 | command.getSigners().size() == 1 &&
32 | command.getSigners().contains(state.getIssuer().getOwningKey()));
33 |
34 | // No state-specific constraints.
35 | return null;
36 | });
37 | } else {
38 | throw new IllegalArgumentException("Unknown command " + command.getValue());
39 | }
40 | }
41 |
42 | public interface Commands extends CommandData {
43 | class Create implements Commands {}
44 | // TODO update and revoke
45 | }}
46 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/src/main/java/com/template/car/state/CarTokenContract.java:
--------------------------------------------------------------------------------
1 | package com.template.car.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import static net.corda.core.contracts.ContractsDSL.requireThat;
9 |
10 | public class CarTokenContract extends EvolvableTokenContract implements Contract {
11 |
12 | @Override
13 | public void additionalCreateChecks(@NotNull final LedgerTransaction tx) {
14 | // Outputs have already been checked as counting exactly 1. If it fails here, it means the output is not
15 | // a CarTokenType.
16 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
17 | requireThat(require -> {
18 | // Validation rules on our fields.
19 | require.using("Mileage must start at 0.",
20 | outputCarTokenType.getMileage() == 0L);
21 | return null;
22 | });
23 | }
24 |
25 | @Override
26 | public void additionalUpdateChecks(@NotNull final LedgerTransaction tx) {
27 | // Inputs and outputs have already been checked as counting exactly 1 each. If any fails here, it means the
28 | // input or output is not a CarTokenType.
29 | final CarTokenType inputCarTokenType = tx.inputsOfType(CarTokenType.class).get(0);
30 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
31 | requireThat(require -> {
32 | // Validation rules on our fields.
33 | require.using("VIN cannot be updated.",
34 | outputCarTokenType.getVin().equals(inputCarTokenType.getVin()));
35 | require.using("Make cannot be updated.",
36 | outputCarTokenType.getMake().equals(inputCarTokenType.getMake()));
37 | require.using("Mileage cannot be decreased.",
38 | outputCarTokenType.getMileage() >= inputCarTokenType.getMileage());
39 | return null;
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/src/main/java/com/template/dummy/state/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class DummyContract implements Contract {
9 |
10 | public static final String DUMMY_CONTRACT_ID = "com.template.dummy.state.DummyContract";
11 |
12 | @Override
13 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
14 | // Do nothing
15 | }
16 |
17 | public interface Commands extends CommandData {
18 | public class Create implements Commands {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/src/main/java/com/template/dummy/state/DummyState.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.BelongsToContract;
4 | import net.corda.core.contracts.ContractState;
5 | import net.corda.core.identity.AbstractParty;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(DummyContract.class)
13 | public class DummyState implements ContractState {
14 | @NotNull
15 | private final AbstractParty lost;
16 | @NotNull
17 | private final AbstractParty seen;
18 |
19 | public DummyState(@NotNull final AbstractParty lost, @NotNull final AbstractParty seen) {
20 | this.lost = lost;
21 | this.seen = seen;
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public List getParticipants() {
27 | return Collections.singletonList(seen);
28 | }
29 |
30 | @NotNull
31 | public AbstractParty getLost() {
32 | return lost;
33 | }
34 |
35 | @NotNull
36 | public AbstractParty getSeen() {
37 | return seen;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object o) {
42 | if (this == o) return true;
43 | if (o == null || getClass() != o.getClass()) return false;
44 | final DummyState that = (DummyState) o;
45 | return lost.equals(that.lost) &&
46 | seen.equals(that.seen);
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | return Objects.hash(lost, seen);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/050-ref-state/contracts/src/test/java/com/template/proposal/state/SalesProposalContractTestHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.proposal.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.FungibleTokenContract;
4 | import com.r3.corda.lib.tokens.contracts.NonFungibleTokenContract;
5 | import com.r3.corda.lib.tokens.contracts.commands.IssueTokenCommand;
6 | import com.r3.corda.lib.tokens.contracts.states.AbstractToken;
7 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
8 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.WireTransaction;
11 | import net.corda.testing.dsl.LedgerDSL;
12 | import net.corda.testing.dsl.TestLedgerDSLInterpreter;
13 | import net.corda.testing.dsl.TestTransactionDSLInterpreter;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.Collections;
17 |
18 | public interface SalesProposalContractTestHelpers {
19 | @NotNull
20 | static WireTransaction issueToken(
21 | @NotNull final LedgerDSL ledger,
22 | @NotNull final Party issuer,
23 | @NotNull final AbstractToken toIssue) {
24 | return ledger.transaction(tx -> {
25 | if (toIssue instanceof NonFungibleToken)
26 | tx.output(NonFungibleTokenContract.Companion.getContractId(), toIssue);
27 | else if (toIssue instanceof FungibleToken)
28 | tx.output(FungibleTokenContract.Companion.getContractId(), toIssue);
29 | else
30 | throw new IllegalArgumentException("Unknown token type " + toIssue.getClass());
31 | tx.command(Collections.singletonList(issuer.getOwningKey()),
32 | new IssueTokenCommand(toIssue.getIssuedTokenType(), Collections.singletonList(0)));
33 | return tx.verifies();
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/050-ref-state/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/050-ref-state/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/050-ref-state/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/050-ref-state/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/050-ref-state/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/050-ref-state/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/050-ref-state/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
2 | usMint="O=US Mint,L=Washington D.C.,C=US"
3 | dmv="O=DMV,L=Austin,C=US"
4 |
--------------------------------------------------------------------------------
/050-ref-state/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/050-ref-state/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
25 | TestCordapp.findCordapp("com.template.car.state"),
26 | TestCordapp.findCordapp("com.template.car.flow")))
27 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
28 | Collections.emptyList(), 4
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/050-ref-state/workflows/src/test/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=Austin, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/050-ref-state/workflows/src/test/java/com/template/car/flow/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/060-time-window/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/060-time-window/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/060-time-window/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/060-time-window/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/060-time-window/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/060-time-window/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/060-time-window/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/060-time-window/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/060-time-window/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/060-time-window/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/060-time-window/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | cordapp "$accounts_release_group:accounts-contracts:$accounts_release_version"
50 | }
51 |
52 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
53 | kotlinOptions {
54 | languageVersion = "1.1"
55 | apiVersion = "1.1"
56 | jvmTarget = "1.8"
57 | javaParameters = true // Useful for reflection.
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/060-time-window/contracts/src/main/java/com/example/contract/KYCContract.java:
--------------------------------------------------------------------------------
1 | package com.example.contract;
2 |
3 | import com.example.state.KYCState;
4 | import net.corda.core.contracts.CommandData;
5 | import net.corda.core.contracts.CommandWithParties;
6 | import net.corda.core.contracts.Contract;
7 | import net.corda.core.transactions.LedgerTransaction;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.List;
11 |
12 | import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
13 | import static net.corda.core.contracts.ContractsDSL.requireThat;
14 |
15 | public class KYCContract implements Contract {
16 | @Override
17 | public void verify(@NotNull final LedgerTransaction tx) throws IllegalArgumentException {
18 | final CommandWithParties command = requireSingleCommand(tx.getCommands(), Commands.class);
19 | if (command.getValue() instanceof Commands.Create) {
20 | requireThat(require -> {
21 | // Generic constraints around the KYC transaction.
22 | require.using("No KYC inputs should be consumed when issuing a KYC.",
23 | tx.inputsOfType(KYCState.class).isEmpty());
24 | final List outList = tx.outputsOfType(KYCState.class);
25 | require.using("Only one output state should be created.",
26 | outList.size() == 1);
27 | final KYCState state = outList.get(0);
28 |
29 | // Signatures constraints.
30 | require.using("The issuer must be the signer.",
31 | command.getSigners().size() == 1 &&
32 | command.getSigners().contains(state.getIssuer().getOwningKey()));
33 |
34 | // No state-specific constraints.
35 | return null;
36 | });
37 | } else {
38 | throw new IllegalArgumentException("Unknown command " + command.getValue());
39 | }
40 | }
41 |
42 | public interface Commands extends CommandData {
43 | class Create implements Commands {}
44 | // TODO update and revoke
45 | }}
46 |
--------------------------------------------------------------------------------
/060-time-window/contracts/src/main/java/com/template/car/state/CarTokenContract.java:
--------------------------------------------------------------------------------
1 | package com.template.car.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import static net.corda.core.contracts.ContractsDSL.requireThat;
9 |
10 | public class CarTokenContract extends EvolvableTokenContract implements Contract {
11 |
12 | @Override
13 | public void additionalCreateChecks(@NotNull final LedgerTransaction tx) {
14 | // Outputs have already been checked as counting exactly 1. If it fails here, it means the output is not
15 | // a CarTokenType.
16 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
17 | requireThat(require -> {
18 | // Validation rules on our fields.
19 | require.using("Mileage must start at 0.",
20 | outputCarTokenType.getMileage() == 0L);
21 | return null;
22 | });
23 | }
24 |
25 | @Override
26 | public void additionalUpdateChecks(@NotNull final LedgerTransaction tx) {
27 | // Inputs and outputs have already been checked as counting exactly 1 each. If any fails here, it means the
28 | // input or output is not a CarTokenType.
29 | final CarTokenType inputCarTokenType = tx.inputsOfType(CarTokenType.class).get(0);
30 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
31 | requireThat(require -> {
32 | // Validation rules on our fields.
33 | require.using("VIN cannot be updated.",
34 | outputCarTokenType.getVin().equals(inputCarTokenType.getVin()));
35 | require.using("Make cannot be updated.",
36 | outputCarTokenType.getMake().equals(inputCarTokenType.getMake()));
37 | require.using("Mileage cannot be decreased.",
38 | outputCarTokenType.getMileage() >= inputCarTokenType.getMileage());
39 | return null;
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/060-time-window/contracts/src/main/java/com/template/dummy/state/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class DummyContract implements Contract {
9 |
10 | public static final String DUMMY_CONTRACT_ID = "com.template.dummy.state.DummyContract";
11 |
12 | @Override
13 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
14 | // Do nothing
15 | }
16 |
17 | public interface Commands extends CommandData {
18 | public class Create implements Commands {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/060-time-window/contracts/src/main/java/com/template/dummy/state/DummyState.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.BelongsToContract;
4 | import net.corda.core.contracts.ContractState;
5 | import net.corda.core.identity.AbstractParty;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(DummyContract.class)
13 | public class DummyState implements ContractState {
14 | @NotNull
15 | private final AbstractParty lost;
16 | @NotNull
17 | private final AbstractParty seen;
18 |
19 | public DummyState(@NotNull final AbstractParty lost, @NotNull final AbstractParty seen) {
20 | this.lost = lost;
21 | this.seen = seen;
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public List getParticipants() {
27 | return Collections.singletonList(seen);
28 | }
29 |
30 | @NotNull
31 | public AbstractParty getLost() {
32 | return lost;
33 | }
34 |
35 | @NotNull
36 | public AbstractParty getSeen() {
37 | return seen;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object o) {
42 | if (this == o) return true;
43 | if (o == null || getClass() != o.getClass()) return false;
44 | final DummyState that = (DummyState) o;
45 | return lost.equals(that.lost) &&
46 | seen.equals(that.seen);
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | return Objects.hash(lost, seen);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/060-time-window/contracts/src/test/java/com/template/proposal/state/SalesProposalContractTestHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.proposal.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.FungibleTokenContract;
4 | import com.r3.corda.lib.tokens.contracts.NonFungibleTokenContract;
5 | import com.r3.corda.lib.tokens.contracts.commands.IssueTokenCommand;
6 | import com.r3.corda.lib.tokens.contracts.states.AbstractToken;
7 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
8 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.WireTransaction;
11 | import net.corda.testing.dsl.LedgerDSL;
12 | import net.corda.testing.dsl.TestLedgerDSLInterpreter;
13 | import net.corda.testing.dsl.TestTransactionDSLInterpreter;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.Collections;
17 |
18 | public interface SalesProposalContractTestHelpers {
19 | @NotNull
20 | static WireTransaction issueToken(
21 | @NotNull final LedgerDSL ledger,
22 | @NotNull final Party issuer,
23 | @NotNull final AbstractToken toIssue) {
24 | return ledger.transaction(tx -> {
25 | if (toIssue instanceof NonFungibleToken)
26 | tx.output(NonFungibleTokenContract.Companion.getContractId(), toIssue);
27 | else if (toIssue instanceof FungibleToken)
28 | tx.output(FungibleTokenContract.Companion.getContractId(), toIssue);
29 | else
30 | throw new IllegalArgumentException("Unknown token type " + toIssue.getClass());
31 | tx.command(Collections.singletonList(issuer.getOwningKey()),
32 | new IssueTokenCommand(toIssue.getIssuedTokenType(), Collections.singletonList(0)));
33 | return tx.verifies();
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/060-time-window/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/060-time-window/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/060-time-window/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/060-time-window/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/060-time-window/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/060-time-window/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/060-time-window/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
2 | usMint="O=US Mint,L=Washington D.C.,C=US"
3 | dmv="O=DMV,L=Austin,C=US"
4 |
--------------------------------------------------------------------------------
/060-time-window/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/060-time-window/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
25 | TestCordapp.findCordapp("com.template.car.state"),
26 | TestCordapp.findCordapp("com.template.car.flow")))
27 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
28 | Collections.emptyList(), 4
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/060-time-window/workflows/src/test/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=Austin, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/060-time-window/workflows/src/test/java/com/template/car/flow/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/070-services/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/070-services/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/070-services/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/070-services/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/070-services/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/070-services/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/070-services/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/070-services/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/070-services/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/070-services/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/070-services/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | cordapp "$accounts_release_group:accounts-contracts:$accounts_release_version"
50 | }
51 |
52 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
53 | kotlinOptions {
54 | languageVersion = "1.1"
55 | apiVersion = "1.1"
56 | jvmTarget = "1.8"
57 | javaParameters = true // Useful for reflection.
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/070-services/contracts/src/main/java/com/example/contract/KYCContract.java:
--------------------------------------------------------------------------------
1 | package com.example.contract;
2 |
3 | import com.example.state.KYCState;
4 | import net.corda.core.contracts.CommandData;
5 | import net.corda.core.contracts.CommandWithParties;
6 | import net.corda.core.contracts.Contract;
7 | import net.corda.core.transactions.LedgerTransaction;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.List;
11 |
12 | import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
13 | import static net.corda.core.contracts.ContractsDSL.requireThat;
14 |
15 | public class KYCContract implements Contract {
16 | @Override
17 | public void verify(@NotNull final LedgerTransaction tx) throws IllegalArgumentException {
18 | final CommandWithParties command = requireSingleCommand(tx.getCommands(), Commands.class);
19 | if (command.getValue() instanceof Commands.Create) {
20 | requireThat(require -> {
21 | // Generic constraints around the KYC transaction.
22 | require.using("No KYC inputs should be consumed when issuing a KYC.",
23 | tx.inputsOfType(KYCState.class).isEmpty());
24 | final List outList = tx.outputsOfType(KYCState.class);
25 | require.using("Only one output state should be created.",
26 | outList.size() == 1);
27 | final KYCState state = outList.get(0);
28 |
29 | // Signatures constraints.
30 | require.using("The issuer must be the signer.",
31 | command.getSigners().size() == 1 &&
32 | command.getSigners().contains(state.getIssuer().getOwningKey()));
33 |
34 | // No state-specific constraints.
35 | return null;
36 | });
37 | } else {
38 | throw new IllegalArgumentException("Unknown command " + command.getValue());
39 | }
40 | }
41 |
42 | public interface Commands extends CommandData {
43 | class Create implements Commands {}
44 | // TODO update and revoke
45 | }}
46 |
--------------------------------------------------------------------------------
/070-services/contracts/src/main/java/com/template/car/state/CarTokenContract.java:
--------------------------------------------------------------------------------
1 | package com.template.car.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import static net.corda.core.contracts.ContractsDSL.requireThat;
9 |
10 | public class CarTokenContract extends EvolvableTokenContract implements Contract {
11 |
12 | @Override
13 | public void additionalCreateChecks(@NotNull final LedgerTransaction tx) {
14 | // Outputs have already been checked as counting exactly 1. If it fails here, it means the output is not
15 | // a CarTokenType.
16 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
17 | requireThat(require -> {
18 | // Validation rules on our fields.
19 | require.using("Mileage must start at 0.",
20 | outputCarTokenType.getMileage() == 0L);
21 | return null;
22 | });
23 | }
24 |
25 | @Override
26 | public void additionalUpdateChecks(@NotNull final LedgerTransaction tx) {
27 | // Inputs and outputs have already been checked as counting exactly 1 each. If any fails here, it means the
28 | // input or output is not a CarTokenType.
29 | final CarTokenType inputCarTokenType = tx.inputsOfType(CarTokenType.class).get(0);
30 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
31 | requireThat(require -> {
32 | // Validation rules on our fields.
33 | require.using("VIN cannot be updated.",
34 | outputCarTokenType.getVin().equals(inputCarTokenType.getVin()));
35 | require.using("Make cannot be updated.",
36 | outputCarTokenType.getMake().equals(inputCarTokenType.getMake()));
37 | require.using("Mileage cannot be decreased.",
38 | outputCarTokenType.getMileage() >= inputCarTokenType.getMileage());
39 | return null;
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/070-services/contracts/src/main/java/com/template/dummy/state/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class DummyContract implements Contract {
9 |
10 | public static final String DUMMY_CONTRACT_ID = "com.template.dummy.state.DummyContract";
11 |
12 | @Override
13 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
14 | // Do nothing
15 | }
16 |
17 | public interface Commands extends CommandData {
18 | public class Create implements Commands {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/070-services/contracts/src/main/java/com/template/dummy/state/DummyState.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.BelongsToContract;
4 | import net.corda.core.contracts.ContractState;
5 | import net.corda.core.identity.AbstractParty;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(DummyContract.class)
13 | public class DummyState implements ContractState {
14 | @NotNull
15 | private final AbstractParty lost;
16 | @NotNull
17 | private final AbstractParty seen;
18 |
19 | public DummyState(@NotNull final AbstractParty lost, @NotNull final AbstractParty seen) {
20 | this.lost = lost;
21 | this.seen = seen;
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public List getParticipants() {
27 | return Collections.singletonList(seen);
28 | }
29 |
30 | @NotNull
31 | public AbstractParty getLost() {
32 | return lost;
33 | }
34 |
35 | @NotNull
36 | public AbstractParty getSeen() {
37 | return seen;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object o) {
42 | if (this == o) return true;
43 | if (o == null || getClass() != o.getClass()) return false;
44 | final DummyState that = (DummyState) o;
45 | return lost.equals(that.lost) &&
46 | seen.equals(that.seen);
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | return Objects.hash(lost, seen);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/070-services/contracts/src/test/java/com/template/proposal/state/SalesProposalContractTestHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.proposal.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.FungibleTokenContract;
4 | import com.r3.corda.lib.tokens.contracts.NonFungibleTokenContract;
5 | import com.r3.corda.lib.tokens.contracts.commands.IssueTokenCommand;
6 | import com.r3.corda.lib.tokens.contracts.states.AbstractToken;
7 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
8 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.WireTransaction;
11 | import net.corda.testing.dsl.LedgerDSL;
12 | import net.corda.testing.dsl.TestLedgerDSLInterpreter;
13 | import net.corda.testing.dsl.TestTransactionDSLInterpreter;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.Collections;
17 |
18 | public interface SalesProposalContractTestHelpers {
19 | @NotNull
20 | static WireTransaction issueToken(
21 | @NotNull final LedgerDSL ledger,
22 | @NotNull final Party issuer,
23 | @NotNull final AbstractToken toIssue) {
24 | return ledger.transaction(tx -> {
25 | if (toIssue instanceof NonFungibleToken)
26 | tx.output(NonFungibleTokenContract.Companion.getContractId(), toIssue);
27 | else if (toIssue instanceof FungibleToken)
28 | tx.output(FungibleTokenContract.Companion.getContractId(), toIssue);
29 | else
30 | throw new IllegalArgumentException("Unknown token type " + toIssue.getClass());
31 | tx.command(Collections.singletonList(issuer.getOwningKey()),
32 | new IssueTokenCommand(toIssue.getIssuedTokenType(), Collections.singletonList(0)));
33 | return tx.verifies();
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/070-services/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/070-services/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/070-services/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/070-services/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/070-services/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/070-services/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/070-services/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
2 | usMint="O=US Mint,L=Washington D.C.,C=US"
3 | dmv="O=DMV,L=Austin,C=US"
4 |
--------------------------------------------------------------------------------
/070-services/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/070-services/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
25 | TestCordapp.findCordapp("com.template.car.state"),
26 | TestCordapp.findCordapp("com.template.car.flow")))
27 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
28 | Collections.emptyList(), 4
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/070-services/workflows/src/test/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=Austin, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/070-services/workflows/src/test/java/com/template/car/flow/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/080-oracle/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/080-oracle/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/080-oracle/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/080-oracle/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/080-oracle/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/080-oracle/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/080-oracle/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/080-oracle/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate
2 |
--------------------------------------------------------------------------------
/080-oracle/README.md:
--------------------------------------------------------------------------------
1 | This represents the step where you have created your own fungible token.
2 |
3 | ## Design decisions
4 |
5 | * We do not assume we need to inform the issuer of the transaction. This not only increases the privacy of transactions, but it also prevents the issuer from being flooded with minute transactions. Of course, when time comes to `Redeem`, the issuer needs to know the whole transaction chain, so privacy is lost there, and the issuer receives a large transaction history.
6 | * A given transaction can only have a single command `Issue`, `Move` or `Redeem`. So it is not possible to reward an issuer `Issue`ing by `Move`ing other tokens to their benefit, for instance.
7 | * We cannot have a `Move` transaction where the sums per issuer are greater than `Long.MAX_VALUE`. It would be possible to have a more complex evaluation that make such a transaction possible.
8 | * We need to collect the `issuer`'s signature when `Redeem`ing. This is a design decision and depends on your specs. If you are ok with the `owner` being the only one necessary to redeem, then you can code it as such. In our case, we could say that the issuer wants to control the total supply, and so wants control over `Issue` and `Redeem` actions. That makes sense in the case of Federal Reserve dollars, air miles or casino chips.
9 | * The issue flow can issue small amounts of token states to the same holder several times, and is not limited to issue 1 state per holder. This can come in handy if we want to bind those states to other actions in parallel.
10 |
11 | ## Preparation
12 |
13 | We decided to delegate build, run and test to Gradle so that the configuration is not shared between `build.gradle` and `.idea` files.
14 |
--------------------------------------------------------------------------------
/080-oracle/config/test/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
8 | >
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/080-oracle/contracts/build.gradle:
--------------------------------------------------------------------------------
1 | if (use_kotlin) apply plugin: 'kotlin'
2 | apply plugin: 'net.corda.plugins.cordapp'
3 | apply plugin: 'net.corda.plugins.cordformation'
4 |
5 | cordapp {
6 | targetPlatformVersion corda_platform_version
7 | minimumPlatformVersion corda_platform_version
8 | contract {
9 | name "Template CorDapp"
10 | vendor "Corda Open Source"
11 | licence "Apache License, Version 2.0"
12 | versionId 1
13 | }
14 | signing {
15 | enabled true
16 | }
17 |
18 | }
19 |
20 | sourceSets {
21 | main{
22 | java {
23 | srcDir 'src/main/java'
24 | java.outputDir = file('bin/main')
25 | }
26 | }
27 | test{
28 | java{
29 | srcDir 'src/test/java'
30 | java.outputDir = file('bin/test')
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | if (use_kotlin) compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
37 |
38 | // Corda dependencies.
39 | cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
40 | cordaRuntime "$corda_release_group:corda:$corda_release_version"
41 |
42 | testCompile "junit:junit:$junit_version"
43 | testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
44 |
45 | // CorDapp dependencies.
46 | cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
47 | cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
48 | cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
49 | cordapp "$accounts_release_group:accounts-contracts:$accounts_release_version"
50 | compile "com.google.guava:guava:$guava_version"
51 | }
52 |
53 | if (use_kotlin) tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
54 | kotlinOptions {
55 | languageVersion = "1.1"
56 | apiVersion = "1.1"
57 | jvmTarget = "1.8"
58 | javaParameters = true // Useful for reflection.
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/example/contract/KYCContract.java:
--------------------------------------------------------------------------------
1 | package com.example.contract;
2 |
3 | import com.example.state.KYCState;
4 | import net.corda.core.contracts.CommandData;
5 | import net.corda.core.contracts.CommandWithParties;
6 | import net.corda.core.contracts.Contract;
7 | import net.corda.core.transactions.LedgerTransaction;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.List;
11 |
12 | import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
13 | import static net.corda.core.contracts.ContractsDSL.requireThat;
14 |
15 | public class KYCContract implements Contract {
16 | @Override
17 | public void verify(@NotNull final LedgerTransaction tx) throws IllegalArgumentException {
18 | final CommandWithParties command = requireSingleCommand(tx.getCommands(), Commands.class);
19 | if (command.getValue() instanceof Commands.Create) {
20 | requireThat(require -> {
21 | // Generic constraints around the KYC transaction.
22 | require.using("No KYC inputs should be consumed when issuing a KYC.",
23 | tx.inputsOfType(KYCState.class).isEmpty());
24 | final List outList = tx.outputsOfType(KYCState.class);
25 | require.using("Only one output state should be created.",
26 | outList.size() == 1);
27 | final KYCState state = outList.get(0);
28 |
29 | // Signatures constraints.
30 | require.using("The issuer must be the signer.",
31 | command.getSigners().size() == 1 &&
32 | command.getSigners().contains(state.getIssuer().getOwningKey()));
33 |
34 | // No state-specific constraints.
35 | return null;
36 | });
37 | } else {
38 | throw new IllegalArgumentException("Unknown command " + command.getValue());
39 | }
40 | }
41 |
42 | public interface Commands extends CommandData {
43 | class Create implements Commands {}
44 | // TODO update and revoke
45 | }}
46 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/example/contract/TemperatureContract.java:
--------------------------------------------------------------------------------
1 | package com.example.contract;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.math.BigDecimal;
9 | import java.util.Objects;
10 |
11 | public class TemperatureContract implements Contract {
12 | @Override
13 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
14 | // TODO
15 | }
16 |
17 | public interface Commands extends CommandData {
18 | class HowWarm implements Commands {
19 | @NotNull
20 | private final BigDecimal lowBound;
21 | @NotNull
22 | private final BigDecimal highBound;
23 |
24 | public HowWarm(
25 | @NotNull final BigDecimal lowBound,
26 | @NotNull final BigDecimal highBound) {
27 | //noinspection ConstantConditions
28 | if (lowBound == null) throw new NullPointerException("lowBound cannot be null");
29 | //noinspection ConstantConditions
30 | if (highBound == null) throw new NullPointerException("highBound cannot be null");
31 | this.lowBound = lowBound;
32 | this.highBound = highBound;
33 | }
34 |
35 | @NotNull
36 | public BigDecimal getLowBound() {
37 | return lowBound;
38 | }
39 |
40 | @NotNull
41 | public BigDecimal getHighBound() {
42 | return highBound;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o) {
47 | if (this == o) return true;
48 | if (o == null || getClass() != o.getClass()) return false;
49 | final HowWarm howWarm = (HowWarm) o;
50 | return lowBound.equals(howWarm.lowBound) &&
51 | highBound.equals(howWarm.highBound);
52 | }
53 |
54 | @Override
55 | public int hashCode() {
56 | return Objects.hash(lowBound, highBound);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/example/oracle/FxOracleUtilities.java:
--------------------------------------------------------------------------------
1 | package com.example.oracle;
2 |
3 | import com.example.contract.FxContract.Commands.Swap;
4 | import net.corda.core.contracts.Command;
5 | import net.corda.core.identity.AbstractParty;
6 | import net.corda.core.transactions.FilteredTransaction;
7 | import net.corda.core.transactions.WireTransaction;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class FxOracleUtilities {
11 |
12 | @NotNull
13 | public static FilteredTransaction filter(
14 | @NotNull final WireTransaction tx,
15 | @NotNull final AbstractParty oracle) {
16 | //noinspection rawtypes
17 | return tx.buildFilteredTransaction(element -> element instanceof Command
18 | && ((Command) element).getSigners().contains(oracle.getOwningKey())
19 | && ((Command) element).getValue() instanceof Swap);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/example/oracle/TemperatureOracleUtilities.java:
--------------------------------------------------------------------------------
1 | package com.example.oracle;
2 |
3 | import com.example.contract.TemperatureContract.Commands.HowWarm;
4 | import net.corda.core.contracts.Command;
5 | import net.corda.core.contracts.TimeWindow;
6 | import net.corda.core.identity.AbstractParty;
7 | import net.corda.core.transactions.FilteredTransaction;
8 | import net.corda.core.transactions.WireTransaction;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class TemperatureOracleUtilities {
12 |
13 | @NotNull
14 | public static FilteredTransaction filter(
15 | @NotNull final WireTransaction tx,
16 | @NotNull final AbstractParty oracle) {
17 | //noinspection rawtypes
18 | return tx.buildFilteredTransaction(element -> element instanceof TimeWindow
19 | || (element instanceof Command
20 | && ((Command) element).getSigners().contains(oracle.getOwningKey())
21 | && ((Command) element).getValue() instanceof HowWarm));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/example/state/FxState.java:
--------------------------------------------------------------------------------
1 | package com.example.state;
2 |
3 | import com.example.contract.FxContract;
4 | import net.corda.core.contracts.BelongsToContract;
5 | import net.corda.core.contracts.ContractState;
6 | import net.corda.core.identity.AbstractParty;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(FxContract.class)
13 | public class FxState implements ContractState {
14 |
15 | @NotNull
16 | private final List participants;
17 |
18 | public FxState(@NotNull final List participants) {
19 | //noinspection ConstantConditions
20 | if (participants == null) throw new NullPointerException("participants cannot be null");
21 | if (participants.isEmpty()) throw new IllegalArgumentException("participants cannot be empty");
22 | this.participants = participants;
23 | }
24 |
25 | @NotNull
26 | @Override
27 | public List getParticipants() {
28 | return participants;
29 | }
30 |
31 | @Override
32 | public boolean equals(Object o) {
33 | if (this == o) return true;
34 | if (o == null || getClass() != o.getClass()) return false;
35 | final FxState that = (FxState) o;
36 | return participants.equals(that.participants);
37 | }
38 |
39 | @Override
40 | public int hashCode() {
41 | return Objects.hash(participants);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/template/car/state/CarTokenContract.java:
--------------------------------------------------------------------------------
1 | package com.template.car.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import static net.corda.core.contracts.ContractsDSL.requireThat;
9 |
10 | public class CarTokenContract extends EvolvableTokenContract implements Contract {
11 |
12 | @Override
13 | public void additionalCreateChecks(@NotNull final LedgerTransaction tx) {
14 | // Outputs have already been checked as counting exactly 1. If it fails here, it means the output is not
15 | // a CarTokenType.
16 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
17 | requireThat(require -> {
18 | // Validation rules on our fields.
19 | require.using("Mileage must start at 0.",
20 | outputCarTokenType.getMileage() == 0L);
21 | return null;
22 | });
23 | }
24 |
25 | @Override
26 | public void additionalUpdateChecks(@NotNull final LedgerTransaction tx) {
27 | // Inputs and outputs have already been checked as counting exactly 1 each. If any fails here, it means the
28 | // input or output is not a CarTokenType.
29 | final CarTokenType inputCarTokenType = tx.inputsOfType(CarTokenType.class).get(0);
30 | final CarTokenType outputCarTokenType = tx.outputsOfType(CarTokenType.class).get(0);
31 | requireThat(require -> {
32 | // Validation rules on our fields.
33 | require.using("VIN cannot be updated.",
34 | outputCarTokenType.getVin().equals(inputCarTokenType.getVin()));
35 | require.using("Make cannot be updated.",
36 | outputCarTokenType.getMake().equals(inputCarTokenType.getMake()));
37 | require.using("Mileage cannot be decreased.",
38 | outputCarTokenType.getMileage() >= inputCarTokenType.getMileage());
39 | return null;
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/template/diligence/state/DiligenceOracleUtilities.java:
--------------------------------------------------------------------------------
1 | package com.template.diligence.state;
2 |
3 | import net.corda.core.contracts.Command;
4 | import net.corda.core.contracts.TimeWindow;
5 | import net.corda.core.identity.AbstractParty;
6 | import net.corda.core.serialization.CordaSerializable;
7 | import net.corda.core.transactions.FilteredTransaction;
8 | import net.corda.core.transactions.WireTransaction;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.time.Duration;
12 |
13 | public class DiligenceOracleUtilities {
14 |
15 | public static final Duration VALID_DURATION = Duration.ofMinutes(10);
16 |
17 | @CordaSerializable
18 | public enum Status {
19 | Linked, Clear
20 | }
21 |
22 | @NotNull
23 | public static FilteredTransaction filter(
24 | @NotNull final WireTransaction tx,
25 | @NotNull final AbstractParty oracle) {
26 | return tx.buildFilteredTransaction(element -> {
27 | //noinspection rawtypes
28 | return (element instanceof Command)
29 | && ((Command) element).getSigners().contains(oracle.getOwningKey())
30 | && (((Command) element).getValue() instanceof DueDiligenceContract.Commands.Certify)
31 | || element instanceof TimeWindow;
32 | });
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/template/dummy/state/DummyContract.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.CommandData;
4 | import net.corda.core.contracts.Contract;
5 | import net.corda.core.transactions.LedgerTransaction;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | public class DummyContract implements Contract {
9 |
10 | public static final String DUMMY_CONTRACT_ID = "com.template.dummy.state.DummyContract";
11 |
12 | @Override
13 | public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
14 | // Do nothing
15 | }
16 |
17 | public interface Commands extends CommandData {
18 | public class Create implements Commands {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/main/java/com/template/dummy/state/DummyState.java:
--------------------------------------------------------------------------------
1 | package com.template.dummy.state;
2 |
3 | import net.corda.core.contracts.BelongsToContract;
4 | import net.corda.core.contracts.ContractState;
5 | import net.corda.core.identity.AbstractParty;
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.Collections;
9 | import java.util.List;
10 | import java.util.Objects;
11 |
12 | @BelongsToContract(DummyContract.class)
13 | public class DummyState implements ContractState {
14 | @NotNull
15 | private final AbstractParty lost;
16 | @NotNull
17 | private final AbstractParty seen;
18 |
19 | public DummyState(@NotNull final AbstractParty lost, @NotNull final AbstractParty seen) {
20 | this.lost = lost;
21 | this.seen = seen;
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public List getParticipants() {
27 | return Collections.singletonList(seen);
28 | }
29 |
30 | @NotNull
31 | public AbstractParty getLost() {
32 | return lost;
33 | }
34 |
35 | @NotNull
36 | public AbstractParty getSeen() {
37 | return seen;
38 | }
39 |
40 | @Override
41 | public boolean equals(Object o) {
42 | if (this == o) return true;
43 | if (o == null || getClass() != o.getClass()) return false;
44 | final DummyState that = (DummyState) o;
45 | return lost.equals(that.lost) &&
46 | seen.equals(that.seen);
47 | }
48 |
49 | @Override
50 | public int hashCode() {
51 | return Objects.hash(lost, seen);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/test/java/com/template/diligence/state/DueDiligenceTests.java:
--------------------------------------------------------------------------------
1 | package com.template.diligence.state;
2 |
3 | import net.corda.core.contracts.UniqueIdentifier;
4 | import net.corda.core.identity.AbstractParty;
5 | import net.corda.core.identity.CordaX500Name;
6 | import net.corda.core.identity.Party;
7 | import net.corda.testing.core.TestIdentity;
8 | import org.junit.Test;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collections;
12 |
13 | import static org.junit.Assert.assertEquals;
14 |
15 | public class DueDiligenceTests {
16 |
17 | private final Party dmv = new TestIdentity(
18 | new CordaX500Name("DMV", "Austin", "US")).getParty();
19 | private final AbstractParty alice = new TestIdentity(
20 | new CordaX500Name("Alice", "London", "GB")).getParty();
21 | private final AbstractParty bob = new TestIdentity(
22 | new CordaX500Name("Bob", "New York", "US")).getParty();
23 | private final AbstractParty carly = new TestIdentity(
24 | new CordaX500Name("Carly", "New York", "US")).getParty();
25 |
26 | @Test(expected = IllegalArgumentException.class)
27 | public void participantsCannotBeEmpty() {
28 | new DueDiligence(new UniqueIdentifier(), new UniqueIdentifier(), dmv, Collections.emptyList());
29 | }
30 |
31 | @Test
32 | public void participantsCanBeSingle() {
33 | final DueDiligence dueDil = new DueDiligence(new UniqueIdentifier(), new UniqueIdentifier(),
34 | dmv, Collections.singletonList(alice));
35 | assertEquals(Collections.singletonList(alice), dueDil.getParticipants());
36 | }
37 |
38 | @Test
39 | public void participantsCanBeTwo() {
40 | final DueDiligence dueDil = new DueDiligence(new UniqueIdentifier(), new UniqueIdentifier(),
41 | dmv, Arrays.asList(alice, bob));
42 | assertEquals(Arrays.asList(alice, bob), dueDil.getParticipants());
43 | }
44 |
45 | @Test
46 | public void participantsCanBeThree() {
47 | final DueDiligence dueDil = new DueDiligence(new UniqueIdentifier(), new UniqueIdentifier(),
48 | dmv, Arrays.asList(alice, bob, carly));
49 | assertEquals(Arrays.asList(alice, bob, carly), dueDil.getParticipants());
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/080-oracle/contracts/src/test/java/com/template/proposal/state/SalesProposalContractTestHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.proposal.state;
2 |
3 | import com.r3.corda.lib.tokens.contracts.FungibleTokenContract;
4 | import com.r3.corda.lib.tokens.contracts.NonFungibleTokenContract;
5 | import com.r3.corda.lib.tokens.contracts.commands.IssueTokenCommand;
6 | import com.r3.corda.lib.tokens.contracts.states.AbstractToken;
7 | import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
8 | import com.r3.corda.lib.tokens.contracts.states.NonFungibleToken;
9 | import net.corda.core.identity.Party;
10 | import net.corda.core.transactions.WireTransaction;
11 | import net.corda.testing.dsl.LedgerDSL;
12 | import net.corda.testing.dsl.TestLedgerDSLInterpreter;
13 | import net.corda.testing.dsl.TestTransactionDSLInterpreter;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | import java.util.Collections;
17 |
18 | public interface SalesProposalContractTestHelpers {
19 | @NotNull
20 | static WireTransaction issueToken(
21 | @NotNull final LedgerDSL ledger,
22 | @NotNull final Party issuer,
23 | @NotNull final AbstractToken toIssue) {
24 | return ledger.transaction(tx -> {
25 | if (toIssue instanceof NonFungibleToken)
26 | tx.output(NonFungibleTokenContract.Companion.getContractId(), toIssue);
27 | else if (toIssue instanceof FungibleToken)
28 | tx.output(FungibleTokenContract.Companion.getContractId(), toIssue);
29 | else
30 | throw new IllegalArgumentException("Unknown token type " + toIssue.getClass());
31 | tx.command(Collections.singletonList(issuer.getOwningKey()),
32 | new IssueTokenCommand(toIssue.getIssuedTokenType(), Collections.singletonList(0)));
33 | return tx.verifies();
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/080-oracle/gradle.properties:
--------------------------------------------------------------------------------
1 | name=Test
2 | group=com.template
3 | version=0.1
4 | kotlin.incremental=false
5 |
--------------------------------------------------------------------------------
/080-oracle/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corda/corda-training-code/99c590d4b56776d08272ca65a6beb80bc33182e8/080-oracle/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/080-oracle/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 25 12:50:39 BST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/080-oracle/repositories.gradle:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenLocal()
3 | mavenCentral()
4 | jcenter()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://software.r3.com/artifactory/corda' }
7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' }
8 | }
9 |
--------------------------------------------------------------------------------
/080-oracle/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'contracts'
2 | include 'workflows'
3 |
4 |
--------------------------------------------------------------------------------
/080-oracle/workflows/res/tokens-workflows.conf:
--------------------------------------------------------------------------------
1 | notary="O=App Notary,L=London,C=GB"
2 | usMint="O=US Mint,L=Washington D.C.,C=US"
3 | dmv="O=DMV,L=Austin,C=US"
4 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/integrationTest/kotlin/com/template/DriverBasedTestK.kt:
--------------------------------------------------------------------------------
1 | package com.template
2 |
3 | import net.corda.core.identity.CordaX500Name
4 | import net.corda.core.utilities.getOrThrow
5 | import net.corda.testing.core.TestIdentity
6 | import net.corda.testing.driver.DriverDSL
7 | import net.corda.testing.driver.DriverParameters
8 | import net.corda.testing.driver.NodeHandle
9 | import net.corda.testing.driver.driver
10 | import org.junit.Test
11 | import java.util.concurrent.Future
12 | import kotlin.test.assertEquals
13 |
14 | class DriverBasedTestK {
15 | private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB"))
16 | private val bankB = TestIdentity(CordaX500Name("BankB", "", "US"))
17 |
18 | @Test
19 | fun `node test`() = withDriver {
20 | // Start a pair of nodes and wait for them both to be ready.
21 | val (partyAHandle, partyBHandle) = startNodes(bankA, bankB)
22 |
23 | // From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
24 | // nodes have started and can communicate.
25 |
26 | // This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
27 | // and other important metrics to ensure that your CorDapp is working as intended.
28 | assertEquals(bankB.name, partyAHandle.resolveName(bankB.name))
29 | assertEquals(bankA.name, partyBHandle.resolveName(bankA.name))
30 | }
31 |
32 | // Runs a test inside the Driver DSL, which provides useful functions for starting nodes, etc.
33 | private fun withDriver(test: DriverDSL.() -> Unit) = driver(
34 | DriverParameters(isDebug = true, startNodesInProcess = true)
35 | ) { test() }
36 |
37 | // Makes an RPC call to retrieve another node's name from the network map.
38 | private fun NodeHandle.resolveName(name: CordaX500Name) = rpc.wellKnownPartyFromX500Name(name)!!.name
39 |
40 | // Resolves a list of futures to a list of the promised values.
41 | private fun List>.waitForAll(): List = map { it.getOrThrow() }
42 |
43 | // Starts multiple nodes simultaneously, then waits for them all to be ready.
44 | private fun DriverDSL.startNodes(vararg identities: TestIdentity) = identities
45 | .map { startNode(providedName = it.name) }
46 | .waitForAll()
47 | }
--------------------------------------------------------------------------------
/080-oracle/workflows/src/main/java/com/template/diligence/flow/DiligenceOracleInternalFlows.java:
--------------------------------------------------------------------------------
1 | package com.template.diligence.flow;
2 |
3 | import co.paralleluniverse.fibers.Suspendable;
4 | import com.template.diligence.state.DiligenceOracleUtilities.Status;
5 | import net.corda.core.contracts.UniqueIdentifier;
6 | import net.corda.core.flows.*;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.security.PublicKey;
10 |
11 | public interface DiligenceOracleInternalFlows {
12 |
13 | class SetOracleKeyFlow extends FlowLogic {
14 |
15 | @NotNull
16 | private final PublicKey oracleKey;
17 |
18 | public SetOracleKeyFlow(@NotNull final PublicKey oracleKey) {
19 | //noinspection ConstantConditions
20 | if (oracleKey == null) throw new NullPointerException("oracleKey cannot be null");
21 | this.oracleKey = oracleKey;
22 | }
23 |
24 | @Suspendable
25 | @Override
26 | public Void call() throws FlowException {
27 | getServiceHub().cordaService(DiligenceOracle.class).setOracleKey(oracleKey);
28 | return null;
29 | }
30 | }
31 |
32 | class SetStatus extends FlowLogic {
33 |
34 | @NotNull
35 | private final UniqueIdentifier tokenId;
36 | @NotNull
37 | private final Status status;
38 |
39 | public SetStatus(
40 | @NotNull UniqueIdentifier tokenId,
41 | @NotNull Status status) {
42 | //noinspection ConstantConditions
43 | if (tokenId == null) throw new NullPointerException("tokenId cannot be null");
44 | //noinspection ConstantConditions
45 | if (status == null) throw new NullPointerException("status cannot be null");
46 | this.tokenId = tokenId;
47 | this.status = status;
48 | }
49 |
50 | @Suspendable
51 | @Override
52 | public Void call() throws FlowException {
53 | getServiceHub().cordaService(DiligenceOracle.class).setStatus(tokenId, status);
54 | return null;
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/main/java/com/template/diligence/flow/DueDiligenceFlowUtils.java:
--------------------------------------------------------------------------------
1 | package com.template.diligence.flow;
2 |
3 | import com.template.diligence.state.DueDiligence;
4 | import net.corda.core.contracts.StateAndRef;
5 | import net.corda.core.flows.FlowException;
6 | import net.corda.core.flows.FlowLogic;
7 | import net.corda.core.node.services.vault.QueryCriteria;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 | import java.util.List;
12 | import java.util.UUID;
13 |
14 | public class DueDiligenceFlowUtils {
15 |
16 | @NotNull
17 | private final FlowLogic> flow;
18 |
19 | public DueDiligenceFlowUtils(@NotNull final FlowLogic> flow) {
20 | this.flow = flow;
21 | }
22 |
23 | @NotNull
24 | public StateAndRef findBy(@NotNull final UUID uuid) throws FlowException {
25 | final QueryCriteria dueDilCriteria = new QueryCriteria.LinearStateQueryCriteria()
26 | .withUuid(Collections.singletonList(uuid));
27 | final List> dueDils = flow.getServiceHub().getVaultService()
28 | .queryBy(DueDiligence.class, dueDilCriteria)
29 | .getStates();
30 | if (dueDils.size() != 1) throw new FlowException("Wrong number of DueDiligence found");
31 | return dueDils.get(0);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/test/java/com/template/NodeDriver.java:
--------------------------------------------------------------------------------
1 | package com.template;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.core.identity.CordaX500Name;
5 | import net.corda.testing.driver.DriverParameters;
6 | import net.corda.testing.driver.NodeParameters;
7 | import net.corda.testing.node.User;
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import java.util.List;
11 | import static net.corda.testing.driver.Driver.driver;
12 |
13 | /**
14 | * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
15 | * environment.
16 | */
17 | public class NodeDriver {
18 | public static void main(String[] args) {
19 | final List rpcUsers =
20 | ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
21 |
22 | driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
23 | try {
24 | dsl.startNode(new NodeParameters()
25 | .withProvidedName(new CordaX500Name("PartyA", "London", "GB"))
26 | .withRpcUsers(rpcUsers)).get();
27 | dsl.startNode(new NodeParameters()
28 | .withProvidedName(new CordaX500Name("PartyB", "New York", "US"))
29 | .withRpcUsers(rpcUsers)).get();
30 | } catch (Throwable e) {
31 | System.err.println("Encountered exception in node startup: " + e.getMessage());
32 | e.printStackTrace();
33 | }
34 |
35 | return null;
36 | }
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/test/java/com/template/car/flow/CarTokenCourseHelpers.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import net.corda.testing.common.internal.ParametersUtilitiesKt;
5 | import net.corda.testing.node.MockNetworkNotarySpec;
6 | import net.corda.testing.node.MockNetworkParameters;
7 | import net.corda.testing.node.TestCordapp;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Collections;
11 |
12 | public interface CarTokenCourseHelpers {
13 | @NotNull
14 | static MockNetworkParameters prepareMockNetworkParameters() {
15 | return new MockNetworkParameters()
16 | .withNotarySpecs(Collections.singletonList(new MockNetworkNotarySpec(CarTokenTypeConstants.NOTARY)))
17 | .withCordappsForAllNodes(ImmutableList.of(
18 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),
19 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.workflows"),
20 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.money"),
21 | TestCordapp.findCordapp("com.r3.corda.lib.tokens.selection"),
22 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
23 | TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
24 | TestCordapp.findCordapp("com.r3.corda.lib.ci.workflows"),
25 | TestCordapp.findCordapp("com.template.car.state"),
26 | TestCordapp.findCordapp("com.template.car.flow"),
27 | TestCordapp.findCordapp("com.template.diligence.state"),
28 | TestCordapp.findCordapp("com.template.diligence.flow")))
29 | .withNetworkParameters(ParametersUtilitiesKt.testNetworkParameters(
30 | Collections.emptyList(), 4
31 | ));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/test/java/com/template/car/flow/CarTokenTypeConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface CarTokenTypeConstants {
6 | CordaX500Name NOTARY = CordaX500Name.parse("O=Gov Notary, L=Washington D.C., C=US");
7 | CordaX500Name DMV = CordaX500Name.parse("O=DMV, L=Austin, C=US");
8 | CordaX500Name BMW_DEALER = CordaX500Name.parse("O=BMW Dealership, L=New York, C=US");
9 | }
10 |
--------------------------------------------------------------------------------
/080-oracle/workflows/src/test/java/com/template/car/flow/UsdTokenConstants.java:
--------------------------------------------------------------------------------
1 | package com.template.car.flow;
2 |
3 | import net.corda.core.identity.CordaX500Name;
4 |
5 | public interface UsdTokenConstants {
6 | CordaX500Name US_MINT = CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US");
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Corda Training Project code
2 |
3 | This repository contains the projects that accompany the Corda Training provided by R3 Corda, which you can find at [https://training.corda.net](https://training.corda.net).
4 |
5 | Each folder in this repository represents one step as described in the course.
6 |
7 | To use Kotlin to compile these, set `useKotlin=true` in `constants.properties`.
8 |
9 | If you have any questions or feedback, you can find us on [https://slack.corda.net](https://slack.corda.net).
10 |
--------------------------------------------------------------------------------
/constants.properties:
--------------------------------------------------------------------------------
1 | cordaReleaseGroup=net.corda
2 | cordaCoreReleaseGroup=net.corda
3 | cordaVersion=4.8.5
4 | cordaCoreVersion=4.8.5
5 | gradlePluginsVersion=5.0.4
6 | # Change to =true if you want to use Kotlin, to =false if you don't
7 | useKotlin=false
8 | kotlinVersion=1.2.71
9 | junitVersion=4.12
10 | quasarVersion=0.7.10
11 | log4jVersion=2.16.0
12 | platformVersion=5
13 | slf4jVersion=1.7.25
14 | nettyVersion=4.1.22.Final
15 | guavaVersion=23.5-jre
16 | # For Tokens SDK
17 | tokensReleaseVersion=1.1
18 | tokensReleaseGroup=com.r3.corda.lib.tokens
19 | # For Confidential Identities
20 | confidentialIdReleaseVersion=1.0
21 | confidentialIdReleaseGroup=com.r3.corda.lib.ci
22 | # For Accounts Library
23 | accountsLibVersion=1.0
24 | accountsLibReleaseGroup=com.r3.corda.lib.accounts
25 |
--------------------------------------------------------------------------------