Still new to Corda.
I am trying to run the Corda Accounts SupplyChain sample via a MockNetwork so that I can debug my flows. In order to do this I need to create the accounts and the run a SendInvoice flow. Here is my Flow Test
class FlowTests {
private lateinit var network: MockNetwork
private lateinit var buyer: StartedMockNode
private lateinit var seller: StartedMockNode
private lateinit var shippingCo: StartedMockNode
@Before
fun setup() {
network = MockNetwork(MockNetworkParameters( networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
cordappsForAllNodes = listOf(
TestCordapp.findCordapp("com.accounts_SupplyChain.contracts"),
TestCordapp.findCordapp("com.accounts_SupplyChain.flows"),
TestCordapp.findCordapp("com.accounts_SupplyChain.schemas"),
TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
// This CorDapp has the missing service from your error message.
TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
TestCordapp.findCordapp("com.r3.corda.lib.ci")
)))
buyer = network.createPartyNode()
seller = network.createPartyNode()
shippingCo = network.createPartyNode()
listOf(buyer,seller,shippingCo).forEach {it.registerInitiatedFlow(SendInvoiceResponder::class.java)}
network.runNetwork()
}
@After
fun tearDown() {
network.stopNodes()
}
@Test
fun Test1() {
// Create Buyer Account
val flow1 = CreateNewAccount("BuyerProcurement")
val future1 = buyer.startFlow(flow1);
// Share the account
val flow3 = ShareAccountTo("BuyerProcurement", shareTo = seller.info.singleIdentity())
val future3 = buyer.startFlow(flow3)
// On Seller Node
val flow7 = CreateNewAccount("SellerSales")
val future7 = seller.startFlow(flow7)
val flow10 = ShareAccountTo("SellerSales", shareTo = buyer.info.singleIdentity())
val future10 = seller.startFlow(flow10)
// Check the accounts are created on the buyer node
val flowView = ViewMyAccounts()
val fut = buyer.startFlow(flowView)
// Check the accounts are creted on the seller Node
val flowView2 = ViewMyAccounts()
val fut2 = seller.startFlow(flowView)
// Now send an Invoice
val sendInvoice = SendInvoice(whoAmI = "SellerSales",whereTo = "BuyerProcurement", amount = 500)
val futureSendInvoice = seller.startFlow(sendInvoice)
network.runNetwork()
val resp1= future1.getOrThrow()
println(resp1.toString())
val resp2 = future7.getOrThrow()
println(resp2.toString())
val buyerAccounts = fut.getOrThrow()
println(buyerAccounts.toString())
val sellerAccounts= fut2.getOrThrow()
println(sellerAccounts.toString())
// Get the invoice Created
val invoiceCreation = futureSendInvoice.getOrThrow()
println(invoiceCreation.toString())
}
}
Not very elegant but it is just a test.
The response I get back is:
BuyerProcurement team's account was created. UUID is : 60d91f03-4112-4c2a-801b-95fe4297bf3b
SellerSales team's account was created. UUID is : 09a4a089-2a98-4148-a00d-252438d816f1
[BuyerProcurement]
[SellerSales]
so I think the accounts are created (maybe not !!). However when running the flow :
val flow10 = ShareAccountTo("SellerSales", shareTo = buyer.info.singleIdentity())
val future10 = seller.startFlow(flow10)
I get an error :
java.util.NoSuchElementException: List is empty.
at kotlin.collections.CollectionsKt___CollectionsKt.single(_Collections.kt:478)
at com.accounts_SupplyChain.flows.SendInvoice.call(SendInvoice.kt:67)
at com.accounts_SupplyChain.flows.SendInvoice.call(SendInvoice.kt:26)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46)
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63)
Setting breakpoints in SendInvoice.kt, I have discovered that it is caused by trying to get the targetAccount (BuyerProcurement) :
@Suspendable
override fun call(): String {
//Generate key for transaction
progressTracker.currentStep = GENERATING_KEYS
val myAccount = accountService.accountInfo(whoAmI).single().state.data
val myKey = subFlow(NewKeyForAccount(myAccount.identifier.id)).owningKey
/// ERROR HERE //////
val targetAccount = accountService.accountInfo(whereTo).single().state.data
val targetAcctAnonymousParty = subFlow(RequestKeyForAccount(targetAccount))
//generating State for transfer
progressTracker.currentStep = GENERATING_TRANSACTION
val output = InvoiceState(amount, AnonymousParty(myKey),targetAcctAnonymousParty,UUID.randomUUID())
val transactionBuilder = TransactionBuilder(serviceHub.networkMapCache.notaryIdentities.first())
transactionBuilder.addOutputState(output)
.addCommand(InvoiceStateContract.Commands.Create(), listOf(targetAcctAnonymousParty.owningKey,myKey))
Does anyone have any ideas. I need to be able to single step through my flows to determine why they don't complete.
If you need to query the account info after some step in test network, you’d call network.runNetwork() every step so that the flow is finalised formally, otherwise you'd not get the data (list is empty)
Thanks. I did a network.runNetwork() after the last shareAccountTo(..) and it is now working.