-
Notifications
You must be signed in to change notification settings - Fork 38
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 a new flow to request for accountInfo by name from a particular host #94
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package com.r3.corda.lib.accounts.workflows.flows | ||
|
||
import co.paralleluniverse.fibers.Suspendable | ||
import com.r3.corda.lib.accounts.contracts.states.AccountInfo | ||
import com.r3.corda.lib.accounts.workflows.internal.accountService | ||
import net.corda.core.contracts.StateAndRef | ||
import net.corda.core.flows.* | ||
import net.corda.core.identity.Party | ||
import net.corda.core.node.StatesToRecord | ||
import net.corda.core.utilities.unwrap | ||
|
||
/** | ||
* This flow can be used to check whether an account is hosted by a particular host by passing account name. If it is then the host will share | ||
* the account info with the requester. This flow should be used in a situation where UUID is difficult to obtain for a given | ||
* account. For e.g: a node can create accounts for its employees using SSN/NIN or employeeId | ||
* | ||
* @property accountName account name to request the [AccountInfo] for hosted at [host] node. | ||
* @property host session to request the [AccountInfo] from. | ||
*/ | ||
class RequestAccountInfoByNameFlow(private val accountName: String, val host: FlowSession) : FlowLogic<AccountInfo?>() { | ||
@Suspendable | ||
override fun call(): AccountInfo? { | ||
val hasAccount = host.sendAndReceive<Boolean>(accountName).unwrap { it } | ||
return if (hasAccount) subFlow(ShareAccountInfoHandlerFlow(host)) else null | ||
} | ||
} | ||
|
||
/** | ||
* Responder flow for [RequestAccountInfoByNameFlow]. | ||
*/ | ||
class RequestAccountInfoByNameHandlerFlow(val otherSession: FlowSession) : FlowLogic<Unit>() { | ||
@Suspendable | ||
override fun call() { | ||
val accountName = otherSession.receive(String::class.java).unwrap {it } | ||
val response = serviceHub.accountService.accountInfo(accountName).find { it.state.data.host == ourIdentity } | ||
if (response == null) { | ||
otherSession.send(false) | ||
} else { | ||
otherSession.send(true) | ||
subFlow(ShareAccountInfoFlow(response, listOf(otherSession))) | ||
} | ||
} | ||
} | ||
|
||
// Initiating versions of the above flows. | ||
|
||
/** | ||
* Shares an [AccountInfo] [StateAndRef] with the supplied [Party]s. The [AccountInfo] is always stored using | ||
* [StatesToRecord.ALL_VISIBLE]. | ||
* | ||
* @property accountName account name to request the [AccountInfo] for. | ||
* @property host [Party] to request the [AccountInfo] from. | ||
*/ | ||
@StartableByRPC | ||
@StartableByService | ||
@InitiatingFlow | ||
class RequestAccountInfoByName(private val accountName: String, val host: Party) : FlowLogic<AccountInfo?>() { | ||
@Suspendable | ||
override fun call(): AccountInfo? { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my humble opinion, you cannot assume how your flow will be used; your code should take care of all cases. For instance, in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the design of this flow is kept in consistent with the design of RequestAccountInfoFlow (here) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see; now I'm curious why they did it that way 🙂 |
||
val session = initiateFlow(host) | ||
return subFlow(RequestAccountInfoByNameFlow(accountName, session)) | ||
} | ||
} | ||
|
||
/** | ||
* Responder flow for [RequestAccountInfoByName]. | ||
*/ | ||
@InitiatedBy(RequestAccountInfoByName::class) | ||
class RequestAccountInfoByNameHandler(val otherSession: FlowSession) : FlowLogic<Unit>() { | ||
@Suspendable | ||
override fun call() { | ||
subFlow(RequestAccountInfoByNameHandlerFlow(otherSession)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package com.r3.corda.lib.accounts.workflows.test | ||
|
||
import com.r3.corda.lib.accounts.workflows.flows.* | ||
import net.corda.testing.common.internal.testNetworkParameters | ||
import net.corda.testing.node.MockNetwork | ||
import net.corda.testing.node.MockNetworkParameters | ||
import net.corda.testing.node.StartedMockNode | ||
import net.corda.testing.node.TestCordapp | ||
import org.junit.After | ||
import org.junit.Assert | ||
import org.junit.Before | ||
import org.junit.Test | ||
import java.lang.AssertionError | ||
import kotlin.test.assertFailsWith | ||
|
||
class RequestAccountInfoByNameTest { | ||
|
||
lateinit var network: MockNetwork | ||
lateinit var nodeA: StartedMockNode | ||
lateinit var nodeB: StartedMockNode | ||
lateinit var nodeC: StartedMockNode | ||
|
||
@Before | ||
fun setup() { | ||
network = MockNetwork( | ||
MockNetworkParameters( | ||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4), | ||
cordappsForAllNodes = listOf( | ||
TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"), | ||
TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows") | ||
) | ||
) | ||
) | ||
nodeA = network.createPartyNode() | ||
nodeB = network.createPartyNode() | ||
nodeC = network.createPartyNode() | ||
|
||
network.runNetwork() | ||
} | ||
|
||
@After | ||
fun tearDown() { | ||
network.stopNodes() | ||
} | ||
|
||
fun StartedMockNode.identity() = info.legalIdentities.first() | ||
|
||
/* | ||
Should return the account info to the host which been requested | ||
*/ | ||
|
||
@Test | ||
fun `should send account info to requester`() { | ||
|
||
//Create an account in host B | ||
val accountB = nodeB.startFlow(CreateAccount("Test_AccountB")).runAndGet(network) | ||
|
||
//Call RequestAccountInfoByName from host A so that host A can request accountB's info | ||
val accountBInfo = nodeA.startFlow(RequestAccountInfoByName("Test_AccountB", nodeB.info.legalIdentities.first())).runAndGet(network) | ||
print(accountBInfo) | ||
|
||
//Checking if accountBInfo's name will be equal to the name with which the account is created | ||
Assert.assertEquals(accountBInfo?.name, "Test_AccountB") | ||
|
||
//Checking if accountBInfo's name will be equal to the name of the account created | ||
Assert.assertEquals(accountBInfo?.name, accountB.state.data.name) | ||
} | ||
|
||
/* | ||
Should throw error if the requested account is not of the host and compare expected account's name with | ||
actual account's name | ||
*/ | ||
|
||
|
||
@Test(expected = AssertionError::class) | ||
fun `should throw error if the name of account passed is wrong and compare the expected account's and actual account's name`() { | ||
|
||
//Create an account in host B | ||
val accountB = nodeB.startFlow(CreateAccount("Test_AccountB")).runAndGet(network) | ||
|
||
//Create an account in host C | ||
val accountC = nodeC.startFlow(CreateAccount("Test_AccountC")).runAndGet(network) | ||
|
||
//To avail the account info of account B for node A, passing name of account C which is wrong name | ||
val accountBInfo = nodeA.startFlow(RequestAccountInfoByName(accountC.state.data.name, nodeB.info.legalIdentities.first())).runAndGet(network) | ||
|
||
//Comparing actual account's name with expected account(account B)'s name | ||
val resultOfAccountIdentifierComparison = Assert.assertEquals(accountBInfo?.name, accountB.state.data.name) | ||
|
||
//result will throw error since the name comparison do not match | ||
assertFailsWith<AssertionError> { resultOfAccountIdentifierComparison } | ||
|
||
} | ||
|
||
/* | ||
Should throw error if the host passed is wrong and compare expected account's name with | ||
actual account's name | ||
*/ | ||
|
||
|
||
@Test(expected = AssertionError::class) | ||
fun `should throw error if the account's host is wrong and compare expected account's and actual account's name`() { | ||
|
||
//Create an account in host A | ||
val accountA = nodeA.startFlow(CreateAccount("Test_AccountA")).runAndGet(network) | ||
|
||
//To get the account info of accountA, passing host as C which is the wrong host | ||
val accountAInfo = nodeB.startFlow(RequestAccountInfoByName(accountA.state.data.name, nodeC.info.legalIdentities.first())).runAndGet(network) | ||
|
||
//Comparing actual account's name with expected account(account A)'s name | ||
val resultOfAccountIdentifierComparison = Assert.assertEquals(accountAInfo?.name, accountA.state.data.name) | ||
|
||
//result will throw error since the name comparison do not match | ||
assertFailsWith<AssertionError> { resultOfAccountIdentifierComparison } | ||
} | ||
|
||
/* | ||
This testcase check when pass wrong name of the account, the result will be null | ||
*/ | ||
|
||
@Test | ||
fun `should return null if account is not found when searching by name`() { | ||
|
||
//Create an account in host C | ||
val accountC = nodeC.startFlow(CreateAccount("Test_AccountC")).runAndGet(network) | ||
|
||
//To avail the account info of account B for node A, passing name of account C which will throw an error | ||
val accountBInfo = nodeA.startFlow(RequestAccountInfoByName(accountC.state.data.name, nodeB.info.legalIdentities.first())).runAndGet(network) | ||
print(accountBInfo) | ||
|
||
//accountBInfo will be null if the name of account entered is wrong | ||
Assert.assertEquals(accountBInfo,null) | ||
|
||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
host.counterParty == ourIdentity
, then just fetch the account from the current node.