├── .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 | 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 | 22 | 23 | -------------------------------------------------------------------------------- /010-empty-project/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 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 | 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 | 22 | 23 | -------------------------------------------------------------------------------- /020-first-token/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | --------------------------------------------------------------------------------