Skip to content

Updated the code to create session only if the recipient is not the node initiating the flow #13

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
52 changes: 52 additions & 0 deletions .idea/codeStyles/Project.xml

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

Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,21 @@ object IssueToken {
@Suspendable
override fun call(): SignedTransaction {
// This is the identity which will be used to issue tokens.
// We also need a session for the other side.
val me: Party = ourIdentity
val ownerSession = initiateFlow(owner)

// Notify the recipient that we'll be issuing them a tokens and advise them of anything they must do, e.g.
// generate a confidential identity for the issuer or sign up for updates for evolvable tokens.
ownerSession.send(TokenIssuanceNotification(anonymous = anonymous))


// We also need a session for the other side.
var ownerSession : FlowSession? = if(me != owner) {
// Notify the recipient that we'll be issuing them a tokens and advise them of anything they must do, e.g.
// generate a confidential identity for the issuer or sign up for updates for evolvable tokens.
val session = initiateFlow(owner)
session.send(TokenIssuanceNotification(anonymous = anonymous))
session
} else null

// This is the recipient of the tokens identity.
val owningParty: AbstractParty = if (anonymous) {
val owningParty: AbstractParty = if(ownerSession != null )
subFlow(RequestConfidentialIdentity.Initiator(ownerSession)).party.anonymise()
} else owner
else owner

// Create the issued token. We add this to the commands for grouping.
val issuedToken: IssuedTokenType<T> = token issuedBy me
Expand Down Expand Up @@ -118,7 +121,11 @@ object IssueToken {
// Sign the transaction. Only Concrete Parties should be used here.
val stx: SignedTransaction = serviceHub.signInitialTransaction(utx)
// No need to pass in a session as there's no counterparty involved.
return subFlow(FinalityFlow(transaction = stx, sessions = listOf(ownerSession)))
return if(me != owner) {
subFlow(FinalityFlow(transaction = stx, sessions = listOf(ownerSession!!)))
} else {
subFlow(FinalityFlow(transaction = stx, sessions = emptyList()))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.r3.corda.sdk.token.workflow.flows

import co.paralleluniverse.fibers.Suspendable
import com.r3.corda.sdk.token.contracts.commands.MoveTokenCommand
import com.r3.corda.sdk.token.contracts.types.TokenPointer
import com.r3.corda.sdk.token.contracts.types.TokenType
import com.r3.corda.sdk.token.contracts.utilities.withNotary
import com.r3.corda.sdk.token.workflow.selection.TokenSelection
Expand Down Expand Up @@ -40,7 +41,11 @@ object MoveToken {
val owningParty: AbstractParty = if (anonymous) {
subFlow(RequestConfidentialIdentity.Initiator(ownerSession)).party.anonymise()
} else owner


if (ownedToken is TokenPointer<*>) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how this will work. I set up the distribution list so that only the creator of the evolvable token type has the distribution list, as they are the only ones which can update the list. Here we are setting up a distribution list on the party which is moving the token (which references the evolvable token type) to another node. So they will have a list but there is no code path in the token SDK currently which would result in this non-issuer party forwarding on the evolvable token updates.... This is where data distribution groups come in. I realise that for now, the functionality here is quite limited until we get data distribution groups. The solution is for all Partys to manually sign up with the maintainer of the evolvable token type using the RequestAdditionToDistributionList flow.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall i remove this AddPartyToDistributionList? I misunderstood the concept and to enable all parties receiving the updates when an update happens on the evolvable token, I added this.

subFlow(AddPartyToDistributionList(owner, ownedToken.pointer.pointer))
}

val (builder, keys) = if (amount == null) {
// The assumption here is that there is only one owned token of a particular type at any one time.
// Double clarify this in the docs to ensure that it is used properly. Either way, this code likely
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ class UpdateEvolvableToken(
val stx: SignedTransaction = serviceHub.signInitialTransaction(utx)
// Get the list of parties on the distribution list for this evolvable token.
val updateSubscribers: List<DistributionRecord> = getDistributionList(serviceHub, new.linearId)
val sessions = updateSubscribers.map { initiateFlow(it.party) }
// Send to all on the list. This could take a while if there are many subscribers!
return subFlow(FinalityFlow(transaction = stx, sessions = sessions))
// If the token is self-issued, subscribers list will contain ourIdentity.
// Remove ourIdentity to avoid initiating a session to itself.
val filteredList = updateSubscribers.filter { it.party != ourIdentity }

return if(filteredList.isNotEmpty()){
val sessions = filteredList.map { initiateFlow(it.party) }
// Send to all on the list. This could take a while if there are many subscribers!
subFlow(FinalityFlow(transaction = stx, sessions = sessions))
} else
subFlow(FinalityFlow(transaction = stx, sessions = emptyList()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import com.r3.corda.sdk.token.money.GBP
import com.r3.corda.sdk.token.workflow.statesAndContracts.House
import com.r3.corda.sdk.token.workflow.utilities.getDistributionList
import com.r3.corda.sdk.token.workflow.utilities.getLinearStateById
import com.r3.corda.sdk.token.workflow.utilities.tokenBalance
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef
import net.corda.core.node.services.queryBy
import net.corda.core.utilities.getOrThrow
import net.corda.finance.AMOUNT
import net.corda.testing.node.StartedMockNode
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -140,5 +142,42 @@ class TokenFlowTests : MockNetworkTest(numberOfNodes = 3) {
val issueTokenB = I.issueTokens(housePointer, A, NOTARY, 50 of housePointer).getOrThrow()
A.watchForTransaction(issueTokenB.id).toCompletableFuture().getOrThrow()
}


@Test
fun `create evolvable token, then self issue`() {
//This is to test the self issue flow, as the counter party session is not created
val house = House("24 Leinster Gardens, Bayswater, London", 900_000.GBP, listOf(I.legalIdentity()))
val createTokenTx = I.createEvolvableToken(house, NOTARY.legalIdentity()).getOrThrow()
val houseToken: StateAndRef<House> = createTokenTx.singleOutput()
val housePointer: TokenPointer<House> = house.toPointer()
I.issueTokens(housePointer, I, NOTARY, 1000 of housePointer).getOrThrow()
val houseTokenState = I.services.vaultService.queryBy<House>().states.single()
assertEquals(houseToken, houseTokenState)
val moveTokenTx = I.moveTokens(housePointer, A, 250 of housePointer, anonymous = true).getOrThrow()
A.watchForTransaction(moveTokenTx.id).getOrThrow()
val issuerTokenBalance = I.services.vaultService.tokenBalance(housePointer)
assertEquals(issuerTokenBalance, AMOUNT(750, housePointer))
val aPartyTokenBalance = A.services.vaultService.tokenBalance(housePointer)
assertEquals(aPartyTokenBalance, AMOUNT(250, housePointer))
}

@Test
fun `create evolvable token, then self issue, move and update`() {
//This is to test the self issue flow, as the counter party session is not created
val house = House("24 Leinster Gardens, Bayswater, London", 900_000.GBP, listOf(I.legalIdentity()))
val createTokenTx = I.createEvolvableToken(house, NOTARY.legalIdentity()).getOrThrow()
val houseToken: StateAndRef<House> = createTokenTx.singleOutput()
val housePointer: TokenPointer<House> = house.toPointer()
I.issueTokens(housePointer, I, NOTARY, 1000 of housePointer).getOrThrow()
val houseTokenState = I.services.vaultService.queryBy<House>().states.single()
assertEquals(houseToken, houseTokenState)
val moveTokenTx = I.moveTokens(housePointer, A, 250 of housePointer, anonymous = true).getOrThrow()
A.watchForTransaction(moveTokenTx.id).getOrThrow()
val proposedToken = house.copy(valuation = 950_000.GBP)
val updateTx = I.updateEvolvableToken(houseToken, proposedToken).getOrThrow()
A.watchForTransaction(updateTx.id).getOrThrow()
val houseQuery= A.services.vaultService.queryBy<House>().states
val updatedHouse = houseQuery.single().state.data
assertEquals(updatedHouse.valuation, proposedToken.valuation)
}
}