diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..1bec35e
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 4702e35..2d79f63 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,11 @@
buildscript {
ext {
corda_release_version = cordaVersion
+ tokens_release_version = '1.1'
+ tokens_release_group = 'com.r3.corda.lib.tokens'
+ confidential_id_release_version = '1.0'
+ confidential_id_release_group = 'com.r3.corda.lib.ci'
+
}
repositories {
@@ -29,6 +34,8 @@ allprojects { //Properties that you need to compile your project (The applicatio
mavenCentral()
maven { url 'https://software.r3.com/artifactory/corda' }
maven { url 'https://jitpack.io' }
+ maven { url 'https://software.r3.com/artifactory/corda-lib' }
+ maven { url 'https://software.r3.com/artifactory/corda-lib-dev' }
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
@@ -84,6 +91,12 @@ dependencies {
cordaCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion"
cordaCompile "org.apache.logging.log4j:log4j-web:$log4jVersion"
cordaCompile "org.slf4j:jul-to-slf4j:$slf4jVersion"
+
+ // Token SDK dependencies.
+ cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
+ cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version"
+ cordapp "$tokens_release_group:tokens-money:$tokens_release_version"
+ cordapp "$tokens_release_group:tokens-selection:$tokens_release_version"
}
task installQuasar(type: Copy) {
@@ -108,6 +121,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
cordapp project(':contracts')
cordapp project(':workflows')
cordapp project(':cbdc-bridge')
+ cordapp("$tokens_release_group:tokens-contracts:$tokens_release_version")
+ cordapp("$tokens_release_group:tokens-workflows:$tokens_release_version")
+ cordapp("$tokens_release_group:tokens-money:$tokens_release_version")
+ cordapp("$tokens_release_group:tokens-selection:$tokens_release_version")
// This configuration is for any CorDapps with custom schema, We will leave this as true to avoid
// problems for developers who are not familiar with Corda. If you are not using custom schemas, you can change
// it to false for quicker project compiling time.
diff --git a/clients/src/main/kotlin/com/cbdc/industria/tech/webserver/Controller.kt b/clients/src/main/kotlin/com/cbdc/industria/tech/webserver/Controller.kt
index 10ba4cb..ac841b9 100644
--- a/clients/src/main/kotlin/com/cbdc/industria/tech/webserver/Controller.kt
+++ b/clients/src/main/kotlin/com/cbdc/industria/tech/webserver/Controller.kt
@@ -1,16 +1,20 @@
package com.cbdc.industria.tech.webserver
-import com.cbdc.industria.tech.flows.CheckPings
+import com.cbdc.industria.tech.flows.*
import com.cbdc.industria.tech.webserver.rpc.NodeRPCConnection
+import net.corda.core.identity.CordaX500Name
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class Controller {
+ val SERVICE_NAMES = listOf("Notary", "Network Map Service")
+
@Autowired
lateinit var node : NodeRPCConnection
@@ -19,8 +23,51 @@ class Controller {
return "Greetings from Spring Boot!";
}
+ /**
+ * Returns the node's name.
+ */
+ @GetMapping("/me")
+ fun whoami() = mapOf("me" to node.proxy.nodeInfo().legalIdentities.first().name)
+
+ /**
+ * Returns all parties registered with the network map service. These names can be used to look up identities using
+ * the identity service.
+ */
+ @GetMapping("/peers")
+ fun getPeers(): Map> {
+ val nodeInfo = node.proxy.networkMapSnapshot()
+ return mapOf("peers" to nodeInfo
+ .map { it.legalIdentities.first().name }
+ //filter out myself, notary and eventual network map started by driver
+ .filter { it.organisation !in (SERVICE_NAMES + node.proxy.nodeInfo().legalIdentities.first().name.organisation) })
+ }
+
@GetMapping("/ping")
fun ping() : List{
return node.proxy.startFlow(::CheckPings).returnValue.getOrThrow()
}
+
+ @GetMapping("/create")
+ fun createEGBP():String{
+ return node.proxy.startFlow(::CreateHouseTokenFlow, "EGBP", 1).returnValue.getOrThrow()
+ }
+
+ @GetMapping("/issue")
+ fun issueEGBP():String{
+ val party = node.proxy.nodeInfo().legalIdentities.first()
+ return node.proxy.startFlow(::IssueHouseTokenFlow, "EGBP", 100, party).returnValue.getOrThrow()
+ }
+
+ @GetMapping("/balance")
+ fun balance(): Long{
+ return node.proxy.startFlow(::GetTokenBalance, "EGBP").returnValue.getOrThrow()
+ }
+
+ @GetMapping("/transfer")
+ fun transferCBOEtoPartyB(): String{
+ val partyName = "O=PartyB,L=New York,C=US"
+ val partyX500Name = CordaX500Name.parse(partyName)
+ val holder = node.proxy.wellKnownPartyFromX500Name(partyX500Name) ?: return "Party named $partyName cannot be found."
+ return node.proxy.startFlow(::MoveHouseTokenFlow, "EGBP", holder, 100).returnValue.getOrThrow()
+ }
}
\ No newline at end of file
diff --git a/contracts/build.gradle b/contracts/build.gradle
index c42fa1a..11c500b 100644
--- a/contracts/build.gradle
+++ b/contracts/build.gradle
@@ -16,6 +16,10 @@ dependencies {
// Corda dependencies.
cordaCompile "$cordaCoreReleaseGroup:corda-core:$cordaCoreVersion"
testCompile "$cordaReleaseGroup:corda-node-driver:$cordaVersion"
+
+ cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version"
+ cordaCompile "$tokens_release_group:tokens-selection:$tokens_release_version"
+
}
quasar {
diff --git a/contracts/src/main/kotlin/com/cbdc/industria/tech/contracts/DigitalPound.kt b/contracts/src/main/kotlin/com/cbdc/industria/tech/contracts/DigitalPound.kt
new file mode 100644
index 0000000..d4398d3
--- /dev/null
+++ b/contracts/src/main/kotlin/com/cbdc/industria/tech/contracts/DigitalPound.kt
@@ -0,0 +1,54 @@
+package com.cbdc.industria.tech.contracts
+
+import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract
+import com.r3.corda.lib.tokens.contracts.FungibleTokenContract
+import com.r3.corda.lib.tokens.contracts.states.EvolvableTokenType
+import com.r3.corda.lib.tokens.contracts.states.FungibleToken
+import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType
+import net.corda.core.contracts.Amount
+import net.corda.core.contracts.BelongsToContract
+import net.corda.core.contracts.Contract
+import net.corda.core.contracts.UniqueIdentifier
+import net.corda.core.identity.AbstractParty
+import net.corda.core.identity.Party
+import net.corda.core.transactions.LedgerTransaction
+
+@BelongsToContract(DigitalPoundStateContract::class)
+data class DigitalPoundState(
+ override val amount: Amount,
+ override val holder: AbstractParty
+):FungibleToken(amount, holder)
+
+class DigitalPoundStateContract : FungibleTokenContract(), Contract{
+ companion object{
+ const val CONTACT_ID = "com.cbdc.industria.tech.contracts.DigitalPoundStateContract"
+ }
+}
+
+
+class HouseTokenStateContract : EvolvableTokenContract(), Contract {
+ companion object {
+ const val CONTRACT_ID = "net.corda.samples.tokenizedhouse.contracts.HouseTokenStateContract"
+ }
+ override fun additionalCreateChecks(tx: LedgerTransaction) {
+ // Write contract validation logic to be performed while creation of token
+ val outputState = tx.getOutput(0) as FungibleHouseTokenState
+ outputState.apply {
+ require(outputState.valuation > 0) {"Valuation must be greater than zero"}
+ }
+ }
+
+ override fun additionalUpdateChecks(tx: LedgerTransaction) {
+ // Write contract validation logic to be performed while updation of token
+ }
+
+}
+
+@BelongsToContract(HouseTokenStateContract::class)
+data class FungibleHouseTokenState(val valuation: Int,
+ val maintainer: Party,
+ override val linearId: UniqueIdentifier,
+ override val fractionDigits: Int,
+ val symbol:String,
+ override val maintainers: List = listOf(maintainer)
+) : EvolvableTokenType()
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index df36bd3..9a48fa5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -22,3 +22,8 @@ jacksonModuleKotlinVersion=2.9.7
detektReleaseGroup=io.gitlab.arturbosch.detekt
detektVersion=1.16.0
+
+
+
+
+
diff --git a/workflows/build.gradle b/workflows/build.gradle
index fe2cbf4..2d4b0e1 100644
--- a/workflows/build.gradle
+++ b/workflows/build.gradle
@@ -49,6 +49,13 @@ dependencies {
// CorDapp dependencies.
cordapp project(":contracts")
cordapp project(":cbdc-bridge")
+
+ cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version"
+ cordapp "$tokens_release_group:tokens-selection:$tokens_release_version"
+ cordapp "$confidential_id_release_group:ci-workflows:$confidential_id_release_version"
+ cordaCompile "$tokens_release_group:tokens-workflows:$tokens_release_version"
+ cordaCompile "$tokens_release_group:tokens-selection:$tokens_release_version"
+
}
task integrationTest(type: Test, dependsOn: []) {
diff --git a/workflows/src/main/kotlin/com/cbdc/industria/tech/flows/MintToken.kt b/workflows/src/main/kotlin/com/cbdc/industria/tech/flows/MintToken.kt
new file mode 100644
index 0000000..ede48ad
--- /dev/null
+++ b/workflows/src/main/kotlin/com/cbdc/industria/tech/flows/MintToken.kt
@@ -0,0 +1,154 @@
+package com.cbdc.industria.tech.flows
+
+import co.paralleluniverse.fibers.Suspendable
+import com.cbdc.industria.tech.contracts.FungibleHouseTokenState
+import com.r3.corda.lib.tokens.contracts.states.FungibleToken
+import com.r3.corda.lib.tokens.contracts.types.TokenPointer
+import com.r3.corda.lib.tokens.contracts.types.TokenType
+import com.r3.corda.lib.tokens.contracts.utilities.issuedBy
+import com.r3.corda.lib.tokens.contracts.utilities.withNotary
+import com.r3.corda.lib.tokens.workflows.flows.rpc.CreateEvolvableTokens
+import com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens
+import com.r3.corda.lib.tokens.workflows.flows.rpc.MoveFungibleTokens
+import com.r3.corda.lib.tokens.workflows.flows.rpc.MoveFungibleTokensHandler
+import com.r3.corda.lib.tokens.workflows.utilities.tokenBalance
+import net.corda.core.contracts.Amount
+import net.corda.core.contracts.UniqueIdentifier
+import net.corda.core.flows.*
+import net.corda.core.identity.CordaX500Name
+import net.corda.core.identity.Party
+import net.corda.core.utilities.ProgressTracker
+
+
+@StartableByRPC
+class MintToken: FlowLogic(){
+
+ @Suspendable
+ override fun call(): Int {
+ TODO()
+ }
+}
+
+
+@StartableByRPC
+class CreateHouseTokenFlow(val symbol: String,
+ val valuationOfHouse:Int) : FlowLogic() {
+ override val progressTracker = ProgressTracker()
+
+ @Suspendable
+ override fun call():String {
+ // Obtain a reference from a notary we wish to use.
+ val notary = serviceHub.networkMapCache.getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB"))
+
+ //create token type
+ val evolvableTokenTypeHouseState = FungibleHouseTokenState(valuationOfHouse,ourIdentity,
+ UniqueIdentifier(),0,symbol)
+
+ //warp it with transaction state specifying the notary
+ val transactionState = evolvableTokenTypeHouseState withNotary notary!!
+
+ //call built in sub flow CreateEvolvableTokens. This can be called via rpc or in unit testing
+ val stx = subFlow(CreateEvolvableTokens(transactionState))
+
+ return "Fungible house token $symbol has created with valuationL: $valuationOfHouse " +
+ "\ntxId: ${stx.id}"
+ }
+}
+
+
+@StartableByRPC
+class IssueHouseTokenFlow(val symbol: String,
+ val quantity: Long,
+ val holder: Party
+) : FlowLogic() {
+ override val progressTracker = ProgressTracker()
+
+ @Suspendable
+ override fun call():String {
+
+ //get house states on ledger with uuid as input tokenId
+ val stateAndRef = serviceHub.vaultService.queryBy(FungibleHouseTokenState::class.java)
+ .states.filter { it.state.data.symbol == symbol }[0]
+
+ //get the RealEstateEvolvableTokenType object
+ val evolvableTokenType = stateAndRef.state.data
+
+ //get the pointer pointer to the house
+ val tokenPointer: TokenPointer<*> = evolvableTokenType.toPointer(evolvableTokenType.javaClass)
+
+ //assign the issuer to the house type who will be issuing the tokens
+ val issuedTokenType = tokenPointer issuedBy ourIdentity
+
+ //specify how much amount to issue to holder
+ val amount = Amount(quantity,issuedTokenType)
+
+ val fungibletoken = FungibleToken(amount,holder)
+
+ val stx = subFlow(IssueTokens(listOf(fungibletoken)))
+ return "Issued $quantity $symbol token to ${holder.name.organisation}"
+ }
+}
+
+
+@InitiatingFlow
+@StartableByRPC
+class GetTokenBalance(val symbol:String) : FlowLogic() {
+ override val progressTracker = ProgressTracker()
+
+ @Suspendable
+ override fun call():Long {
+ //get house states on ledger with uuid as input tokenId
+ val stateAndRef = serviceHub.vaultService.queryBy(FungibleHouseTokenState::class.java)
+ .states.filter { it.state.data.symbol == symbol }[0]
+
+ //get the Token State object
+ val evolvableTokenType = stateAndRef.state.data
+ //get the pointer pointer to the house
+ val tokenPointer = evolvableTokenType.toPointer(evolvableTokenType.javaClass)
+
+ //retrieve amount
+ val amount: Amount = serviceHub.vaultService.tokenBalance(tokenPointer)
+
+ //return "\n You currently have " + amount.quantity + " " + symbol + " Tokens issued by "+evolvableTokenType.maintainer.name.organisation+"\n";
+ return amount.quantity
+ }
+}
+
+
+@InitiatingFlow
+@StartableByRPC
+class MoveHouseTokenFlow(val symbol: String,
+ val holder: Party,
+ val quantity: Long) : FlowLogic() {
+ override val progressTracker = ProgressTracker()
+
+ @Suspendable
+ override fun call():String {
+ //get house states on ledger with uuid as input tokenId
+ val stateAndRef = serviceHub.vaultService.queryBy(FungibleHouseTokenState::class.java)
+ .states.filter { it.state.data.symbol == symbol }[0]
+
+ //get the RealEstateEvolvableTokenType object
+ val evolvableTokenType = stateAndRef.state.data
+
+ //get the pointer pointer to the house
+ val tokenPointer: TokenPointer = evolvableTokenType.toPointer(evolvableTokenType.javaClass)
+
+ //specify how much amount to issue to holder
+ val amount:Amount = Amount(quantity,tokenPointer)
+ val stx = subFlow(MoveFungibleTokens(amount,holder))
+
+ return "Moved $quantity $symbol token(s) to ${holder.name.organisation}"+
+ "\ntxId: ${stx.id}"
+ }
+}
+
+@InitiatedBy(MoveHouseTokenFlow::class)
+class MoveHouseTokenFlowResponder(val counterpartySession: FlowSession) : FlowLogic() {
+ @Suspendable
+ override fun call() {
+ // Simply use the MoveFungibleTokensHandler as the responding flow
+ return subFlow(MoveFungibleTokensHandler(counterpartySession))
+
+ }
+}
\ No newline at end of file