diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..6d799a05 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/IssueToken.kt b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/IssueToken.kt index 78b27a93..c0319e4f 100644 --- a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/IssueToken.kt +++ b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/IssueToken.kt @@ -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 = token issuedBy me @@ -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())) + } } } diff --git a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/MoveToken.kt b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/MoveToken.kt index 2e97d38b..4cdcc5e3 100644 --- a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/MoveToken.kt +++ b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/MoveToken.kt @@ -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 @@ -40,7 +41,11 @@ object MoveToken { val owningParty: AbstractParty = if (anonymous) { subFlow(RequestConfidentialIdentity.Initiator(ownerSession)).party.anonymise() } else owner - + + if (ownedToken is TokenPointer<*>) { + 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 diff --git a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/UpdateEvolvableToken.kt b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/UpdateEvolvableToken.kt index 810ae266..295c681f 100644 --- a/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/UpdateEvolvableToken.kt +++ b/workflow/src/main/kotlin/com/r3/corda/sdk/token/workflow/flows/UpdateEvolvableToken.kt @@ -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 = 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())) } } \ No newline at end of file diff --git a/workflow/src/test/kotlin/com/r3/corda/sdk/token/workflow/TokenFlowTests.kt b/workflow/src/test/kotlin/com/r3/corda/sdk/token/workflow/TokenFlowTests.kt index 2ff12e36..3b080130 100644 --- a/workflow/src/test/kotlin/com/r3/corda/sdk/token/workflow/TokenFlowTests.kt +++ b/workflow/src/test/kotlin/com/r3/corda/sdk/token/workflow/TokenFlowTests.kt @@ -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 @@ -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 = createTokenTx.singleOutput() + val housePointer: TokenPointer = house.toPointer() + I.issueTokens(housePointer, I, NOTARY, 1000 of housePointer).getOrThrow() + val houseTokenState = I.services.vaultService.queryBy().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 = createTokenTx.singleOutput() + val housePointer: TokenPointer = house.toPointer() + I.issueTokens(housePointer, I, NOTARY, 1000 of housePointer).getOrThrow() + val houseTokenState = I.services.vaultService.queryBy().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().states + val updatedHouse = houseQuery.single().state.data + assertEquals(updatedHouse.valuation, proposedToken.valuation) + } } \ No newline at end of file