Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add token sdk + dedicated apis #19

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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<String, List<CordaX500Name>> {
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<String>{
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()
}
}
4 changes: 4 additions & 0 deletions contracts/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IssuedTokenType>,
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<Party> = listOf(maintainer)
) : EvolvableTokenType()
5 changes: 5 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ jacksonModuleKotlinVersion=2.9.7

detektReleaseGroup=io.gitlab.arturbosch.detekt
detektVersion=1.16.0





7 changes: 7 additions & 0 deletions workflows/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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: []) {
Expand Down
154 changes: 154 additions & 0 deletions workflows/src/main/kotlin/com/cbdc/industria/tech/flows/MintToken.kt
Original file line number Diff line number Diff line change
@@ -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<Int>(){

@Suspendable
override fun call(): Int {
TODO()
}
}


@StartableByRPC
class CreateHouseTokenFlow(val symbol: String,
val valuationOfHouse:Int) : FlowLogic<String>() {
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<String>() {
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<Long>() {
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<TokenType> = 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<String>() {
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<FungibleHouseTokenState> = evolvableTokenType.toPointer(evolvableTokenType.javaClass)

//specify how much amount to issue to holder
val amount:Amount<TokenType> = 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<Unit>() {
@Suspendable
override fun call() {
// Simply use the MoveFungibleTokensHandler as the responding flow
return subFlow(MoveFungibleTokensHandler(counterpartySession))

}
}