mirror of
https://github.com/corda/corda.git
synced 2025-02-03 01:31:24 +00:00
Clean up IssuerFlow
* Switch to using anonymous party as recipient * Enable anonymisation for issuance as well as move in issuer flows. * Pass notary into issuer flow rather than taking a notary at random from the network map. * Enable anonymisation in Bank of Corda RPC test * Parameterize issuer flow tests into anonymous and deanonymised versions
This commit is contained in:
parent
f6aa672215
commit
773aa28873
@ -9,6 +9,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
@ -303,6 +304,13 @@ interface CordaRPCOps : RPCOps {
|
||||
|
||||
/** Enumerates the class names of the flows that this node knows about. */
|
||||
fun registeredFlows(): List<String>
|
||||
|
||||
/**
|
||||
* Returns a node's identity from the network map cache, where known.
|
||||
*
|
||||
* @return the node info if available.
|
||||
*/
|
||||
fun nodeIdentityFromParty(party: AbstractParty): NodeInfo?
|
||||
}
|
||||
|
||||
inline fun <reified T : ContractState> CordaRPCOps.vaultQueryBy(criteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(),
|
||||
@ -371,6 +379,17 @@ inline fun <T : Any, A, B, C, D, E, reified R : FlowLogic<T>> CordaRPCOps.startF
|
||||
arg4: E
|
||||
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4)
|
||||
|
||||
inline fun <T : Any, A, B, C, D, E, F, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
flowConstructor: (A, B, C, D, E, F) -> R,
|
||||
arg0: A,
|
||||
arg1: B,
|
||||
arg2: C,
|
||||
arg3: D,
|
||||
arg4: E,
|
||||
arg5: F
|
||||
): FlowHandle<T> = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
|
||||
/**
|
||||
* Same again, except this time with progress-tracking enabled.
|
||||
*/
|
||||
|
@ -24,6 +24,7 @@ object IssuerFlow {
|
||||
data class IssuanceRequestState(val amount: Amount<Currency>,
|
||||
val issueToParty: Party,
|
||||
val issuerPartyRef: OpaqueBytes,
|
||||
val notaryParty: Party,
|
||||
val anonymous: Boolean)
|
||||
|
||||
/**
|
||||
@ -39,11 +40,12 @@ object IssuerFlow {
|
||||
val issueToParty: Party,
|
||||
val issueToPartyRef: OpaqueBytes,
|
||||
val issuerBankParty: Party,
|
||||
val notaryParty: Party,
|
||||
val anonymous: Boolean) : FlowLogic<AbstractCashFlow.Result>() {
|
||||
@Suspendable
|
||||
@Throws(CashException::class)
|
||||
override fun call(): AbstractCashFlow.Result {
|
||||
val issueRequest = IssuanceRequestState(amount, issueToParty, issueToPartyRef, anonymous)
|
||||
val issueRequest = IssuanceRequestState(amount, issueToParty, issueToPartyRef, notaryParty, anonymous)
|
||||
return sendAndReceive<AbstractCashFlow.Result>(issuerBankParty, issueRequest).unwrap { res ->
|
||||
val tx = res.stx.tx
|
||||
val expectedAmount = Amount(amount.quantity, Issued(issuerBankParty.ref(issueToPartyRef), amount.token))
|
||||
@ -86,7 +88,7 @@ object IssuerFlow {
|
||||
it
|
||||
}
|
||||
// TODO: parse request to determine Asset to issue
|
||||
val txn = issueCashTo(issueRequest.amount, issueRequest.issueToParty, issueRequest.issuerPartyRef, issueRequest.anonymous)
|
||||
val txn = issueCashTo(issueRequest.amount, issueRequest.issueToParty, issueRequest.issuerPartyRef, issueRequest.notaryParty, issueRequest.anonymous)
|
||||
progressTracker.currentStep = SENDING_CONFIRM
|
||||
send(otherParty, txn)
|
||||
return txn.stx
|
||||
@ -96,13 +98,12 @@ object IssuerFlow {
|
||||
private fun issueCashTo(amount: Amount<Currency>,
|
||||
issueTo: Party,
|
||||
issuerPartyRef: OpaqueBytes,
|
||||
notaryParty: Party,
|
||||
anonymous: Boolean): AbstractCashFlow.Result {
|
||||
// TODO: pass notary in as request parameter
|
||||
val notaryParty = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity
|
||||
// invoke Cash subflow to issue Asset
|
||||
progressTracker.currentStep = ISSUING
|
||||
val issueRecipient = serviceHub.myInfo.legalIdentity
|
||||
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, issueRecipient, notaryParty, anonymous = false)
|
||||
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, issueRecipient, notaryParty, anonymous)
|
||||
val issueTx = subFlow(issueCashFlow)
|
||||
// NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
|
||||
// short-circuit when issuing to self
|
||||
|
@ -5,6 +5,7 @@ import net.corda.contracts.asset.sumCashBy
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -49,14 +50,14 @@ object TwoPartyTradeFlow {
|
||||
data class SellerTradeInfo(
|
||||
val assetForSale: StateAndRef<OwnableState>,
|
||||
val price: Amount<Currency>,
|
||||
val sellerOwnerKey: PublicKey
|
||||
val sellerOwner: AbstractParty
|
||||
)
|
||||
|
||||
open class Seller(val otherParty: Party,
|
||||
val notaryNode: NodeInfo,
|
||||
val assetToSell: StateAndRef<OwnableState>,
|
||||
val price: Amount<Currency>,
|
||||
val myKey: PublicKey,
|
||||
val me: AbstractParty,
|
||||
override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() {
|
||||
|
||||
companion object {
|
||||
@ -75,7 +76,7 @@ object TwoPartyTradeFlow {
|
||||
override fun call(): SignedTransaction {
|
||||
progressTracker.currentStep = AWAITING_PROPOSAL
|
||||
// Make the first message we'll send to kick off the flow.
|
||||
val hello = SellerTradeInfo(assetToSell, price, myKey)
|
||||
val hello = SellerTradeInfo(assetToSell, price, me)
|
||||
// What we get back from the other side is a transaction that *might* be valid and acceptable to us,
|
||||
// but we must check it out thoroughly before we sign!
|
||||
send(otherParty, hello)
|
||||
@ -85,7 +86,7 @@ object TwoPartyTradeFlow {
|
||||
// DOCSTART 5
|
||||
val signTransactionFlow = object : SignTransactionFlow(otherParty, VERIFYING_AND_SIGNING.childProgressTracker()) {
|
||||
override fun checkTransaction(stx: SignedTransaction) {
|
||||
if (stx.tx.outputs.map { it.data }.sumCashBy(AnonymousParty(myKey)).withoutIssuer() != price)
|
||||
if (stx.tx.outputs.map { it.data }.sumCashBy(me).withoutIssuer() != price)
|
||||
throw FlowException("Transaction is not sending us the right amount of cash")
|
||||
}
|
||||
}
|
||||
@ -181,7 +182,7 @@ object TwoPartyTradeFlow {
|
||||
val ptx = TransactionType.General.Builder(notary)
|
||||
|
||||
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
||||
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, AnonymousParty(tradeRequest.sellerOwnerKey))
|
||||
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, tradeRequest.sellerOwner)
|
||||
|
||||
// Add inputs/outputs/a command for the movement of the asset.
|
||||
tx.addInputState(tradeRequest.assetForSale)
|
||||
|
@ -22,10 +22,21 @@ import net.corda.testing.node.MockNetwork.MockNode
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.util.*
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class IssuerFlowTest {
|
||||
@RunWith(Parameterized::class)
|
||||
class IssuerFlowTest(val anonymous: Boolean) {
|
||||
companion object {
|
||||
@Parameterized.Parameters
|
||||
@JvmStatic
|
||||
fun data(): Collection<Array<Boolean>> {
|
||||
return listOf(arrayOf(false), arrayOf(true))
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var mockNet: MockNetwork
|
||||
lateinit var notaryNode: MockNode
|
||||
lateinit var bankOfCordaNode: MockNode
|
||||
@ -46,6 +57,7 @@ class IssuerFlowTest {
|
||||
|
||||
@Test
|
||||
fun `test issuer flow`() {
|
||||
val notary = notaryNode.services.myInfo.notaryIdentity
|
||||
val (vaultUpdatesBoc, vaultUpdatesBankClient) = bankOfCordaNode.database.transaction {
|
||||
// Register for vault updates
|
||||
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
@ -54,7 +66,7 @@ class IssuerFlowTest {
|
||||
|
||||
// using default IssueTo Party Reference
|
||||
val issuerResult = runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, 1000000.DOLLARS,
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123))
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123), notary)
|
||||
issuerResult.get()
|
||||
|
||||
Pair(vaultUpdatesBoc, vaultUpdatesBankClient)
|
||||
@ -68,8 +80,7 @@ class IssuerFlowTest {
|
||||
require(update.consumed.isEmpty()) { "Expected 0 consumed states, actual: $update" }
|
||||
require(update.produced.size == 1) { "Expected 1 produced states, actual: $update" }
|
||||
val issued = update.produced.single().state.data as Cash.State
|
||||
require(issued.owner == bankOfCordaNode.info.legalIdentity)
|
||||
require(issued.owner != bankClientNode.info.legalIdentity)
|
||||
require(issued.owner.owningKey in bankOfCordaNode.services.keyManagementService.keys)
|
||||
},
|
||||
// MOVE
|
||||
expect { update ->
|
||||
@ -86,29 +97,31 @@ class IssuerFlowTest {
|
||||
require(update.consumed.isEmpty()) { update.consumed.size }
|
||||
require(update.produced.size == 1) { update.produced.size }
|
||||
val paidState = update.produced.single().state.data as Cash.State
|
||||
require(paidState.owner == bankClientNode.info.legalIdentity)
|
||||
require(paidState.owner.owningKey in bankClientNode.services.keyManagementService.keys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test issuer flow rejects restricted`() {
|
||||
val notary = notaryNode.services.myInfo.notaryIdentity
|
||||
// try to issue an amount of a restricted currency
|
||||
assertFailsWith<FlowException> {
|
||||
runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, Amount(100000L, currency("BRL")),
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123)).getOrThrow()
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123), notary).getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test issue flow to self`() {
|
||||
val notary = notaryNode.services.myInfo.notaryIdentity
|
||||
val vaultUpdatesBoc = bankOfCordaNode.database.transaction {
|
||||
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val (_, vaultUpdatesBoc) = bankOfCordaNode.services.vaultQueryService.trackBy<Cash.State>(criteria)
|
||||
|
||||
// using default IssueTo Party Reference
|
||||
runIssuerAndIssueRequester(bankOfCordaNode, bankOfCordaNode, 1000000.DOLLARS,
|
||||
bankOfCordaNode.info.legalIdentity, OpaqueBytes.of(123)).getOrThrow()
|
||||
bankOfCordaNode.info.legalIdentity, OpaqueBytes.of(123), notary).getOrThrow()
|
||||
vaultUpdatesBoc
|
||||
}
|
||||
|
||||
@ -126,12 +139,13 @@ class IssuerFlowTest {
|
||||
|
||||
@Test
|
||||
fun `test concurrent issuer flow`() {
|
||||
val notary = notaryNode.services.myInfo.notaryIdentity
|
||||
// this test exercises the Cashflow issue and move subflows to ensure consistent spending of issued states
|
||||
val amount = 10000.DOLLARS
|
||||
val amounts = calculateRandomlySizedAmounts(10000.DOLLARS, 10, 10, Random())
|
||||
val handles = amounts.map { pennies ->
|
||||
runIssuerAndIssueRequester(bankOfCordaNode, bankClientNode, Amount(pennies, amount.token),
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123))
|
||||
bankClientNode.info.legalIdentity, OpaqueBytes.of(123), notary)
|
||||
}
|
||||
handles.forEach {
|
||||
require(it.get().stx is SignedTransaction)
|
||||
@ -141,11 +155,12 @@ class IssuerFlowTest {
|
||||
private fun runIssuerAndIssueRequester(issuerNode: MockNode,
|
||||
issueToNode: MockNode,
|
||||
amount: Amount<Currency>,
|
||||
party: Party,
|
||||
ref: OpaqueBytes): ListenableFuture<AbstractCashFlow.Result> {
|
||||
val issueToPartyAndRef = party.ref(ref)
|
||||
val issueRequest = IssuanceRequester(amount, party, issueToPartyAndRef.reference, issuerNode.info.legalIdentity,
|
||||
anonymous = false)
|
||||
issueToParty: Party,
|
||||
ref: OpaqueBytes,
|
||||
notaryParty: Party): ListenableFuture<AbstractCashFlow.Result> {
|
||||
val issueToPartyAndRef = issueToParty.ref(ref)
|
||||
val issueRequest = IssuanceRequester(amount, issueToParty, issueToPartyAndRef.reference, issuerNode.info.legalIdentity, notaryParty,
|
||||
anonymous)
|
||||
return issueToNode.services.startFlow(issueRequest).resultFuture
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -178,6 +179,7 @@ class CordaRPCOpsImpl(
|
||||
override fun partyFromName(name: String) = services.identityService.partyFromName(name)
|
||||
override fun partyFromX500Name(x500Name: X500Name) = services.identityService.partyFromX500Name(x500Name)
|
||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> = services.identityService.partiesFromName(query, exactMatch)
|
||||
override fun nodeIdentityFromParty(party: AbstractParty): NodeInfo? = services.networkMapCache.getNodeByLegalIdentity(party)
|
||||
|
||||
override fun registeredFlows(): List<String> = services.rpcFlows.map { it.name }.sorted()
|
||||
|
||||
|
@ -509,12 +509,13 @@ class TwoPartyTradeFlowTests {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
send(buyer, Pair(notary.notaryIdentity, price))
|
||||
val key = serviceHub.keyManagementService.freshKey()
|
||||
return subFlow(Seller(
|
||||
buyer,
|
||||
notary,
|
||||
assetToSell,
|
||||
price,
|
||||
serviceHub.legalIdentityKey))
|
||||
AnonymousParty(key)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@ import net.corda.bank.api.BankOfCordaClientApi
|
||||
import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.testing.BOC
|
||||
import net.corda.testing.driver.driver
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -19,9 +19,9 @@ class BankOfCordaHttpAPITest {
|
||||
startNode(BOC.name, setOf(ServiceInfo(SimpleNotaryService.type))),
|
||||
startNode(BIGCORP_LEGAL_NAME)
|
||||
).getOrThrow()
|
||||
val anonymous = true
|
||||
val anonymous = false
|
||||
val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress
|
||||
assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, anonymous)))
|
||||
assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, BOC.name, anonymous)))
|
||||
}, isDebug = true)
|
||||
}
|
||||
}
|
||||
|
@ -44,14 +44,14 @@ class BankOfCordaRPCClientTest {
|
||||
val (_, vaultUpdatesBigCorp) = bigCorpProxy.vaultTrackByCriteria<Cash.State>(Cash.State::class.java, criteria)
|
||||
|
||||
// Kick-off actual Issuer Flow
|
||||
// TODO: Update checks below to reflect states consumed/produced under anonymisation
|
||||
val anonymous = false
|
||||
val anonymous = true
|
||||
bocProxy.startFlow(
|
||||
::IssuanceRequester,
|
||||
1000.DOLLARS,
|
||||
nodeBigCorporation.nodeInfo.legalIdentity,
|
||||
BIG_CORP_PARTY_REF,
|
||||
nodeBankOfCorda.nodeInfo.legalIdentity,
|
||||
nodeBankOfCorda.nodeInfo.notaryIdentity,
|
||||
anonymous).returnValue.getOrThrow()
|
||||
|
||||
// Check Bank of Corda Vault Updates
|
||||
|
@ -68,7 +68,7 @@ private class BankOfCordaDriver {
|
||||
} else {
|
||||
try {
|
||||
val anonymous = true
|
||||
val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, "1", BOC.name, anonymous)
|
||||
val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name, anonymous)
|
||||
when (role) {
|
||||
Role.ISSUE_CASH_RPC -> {
|
||||
println("Requesting Cash via RPC ...")
|
||||
|
@ -40,11 +40,14 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
|
||||
?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service")
|
||||
val issuerBankParty = proxy.partyFromX500Name(params.issuerBankName)
|
||||
?: throw Exception("Unable to locate ${params.issuerBankName} in Network Map Service")
|
||||
val notaryParty = proxy.partyFromX500Name(params.notaryName)
|
||||
?: throw Exception("Unable to locate ${params.notaryName} in Network Map Service")
|
||||
|
||||
val amount = Amount(params.amount, currency(params.currency))
|
||||
val issuerToPartyRef = OpaqueBytes.of(params.issueToPartyRefAsString.toByte())
|
||||
|
||||
return proxy.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty, params.anonymous).returnValue.getOrThrow().stx
|
||||
return proxy.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty, notaryParty, params.anonymous)
|
||||
.returnValue.getOrThrow().stx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.corda.bank.api
|
||||
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.currency
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.startFlow
|
||||
@ -21,6 +20,7 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) {
|
||||
data class IssueRequestParams(val amount: Long, val currency: String,
|
||||
val issueToPartyName: X500Name, val issueToPartyRefAsString: String,
|
||||
val issuerBankName: X500Name,
|
||||
val notaryName: X500Name,
|
||||
val anonymous: Boolean)
|
||||
|
||||
private companion object {
|
||||
@ -43,9 +43,11 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) {
|
||||
fun issueAssetRequest(params: IssueRequestParams): Response {
|
||||
// Resolve parties via RPC
|
||||
val issueToParty = rpc.partyFromX500Name(params.issueToPartyName)
|
||||
?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service")
|
||||
?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issueToPartyName} in Network Map Service").build()
|
||||
val issuerBankParty = rpc.partyFromX500Name(params.issuerBankName)
|
||||
?: throw Exception("Unable to locate ${params.issuerBankName} in Network Map Service")
|
||||
?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issuerBankName} in Network Map Service").build()
|
||||
val notaryParty = rpc.partyFromX500Name(params.notaryName)
|
||||
?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.notaryName} in Network Map Service").build()
|
||||
|
||||
val amount = Amount(params.amount, currency(params.currency))
|
||||
val issuerToPartyRef = OpaqueBytes.of(params.issueToPartyRefAsString.toByte())
|
||||
@ -53,13 +55,13 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) {
|
||||
|
||||
// invoke client side of Issuer Flow: IssuanceRequester
|
||||
// The line below blocks and waits for the future to resolve.
|
||||
val status = try {
|
||||
rpc.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty, anonymous).returnValue.getOrThrow()
|
||||
return try {
|
||||
rpc.startFlow(::IssuanceRequester, amount, issueToParty, issuerToPartyRef, issuerBankParty, notaryParty, anonymous).returnValue.getOrThrow()
|
||||
logger.info("Issue request completed successfully: $params")
|
||||
Response.Status.CREATED
|
||||
} catch (e: FlowException) {
|
||||
Response.Status.BAD_REQUEST
|
||||
Response.status(Response.Status.CREATED).build()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Issue request failed: ${e}", e)
|
||||
Response.status(Response.Status.FORBIDDEN).build()
|
||||
}
|
||||
return Response.status(status).build()
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import net.corda.core.utilities.Emoji
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.flows.IssuerFlow.IssuanceRequester
|
||||
import net.corda.testing.BOC
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.traderdemo.flow.SellerFlow
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import java.util.*
|
||||
@ -45,12 +46,16 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) {
|
||||
|
||||
fun runBuyer(amount: Amount<Currency> = 30000.DOLLARS, anonymous: Boolean = true) {
|
||||
val bankOfCordaParty = rpc.partyFromX500Name(BOC.name)
|
||||
?: throw Exception("Unable to locate ${BOC.name} in Network Map Service")
|
||||
?: throw IllegalStateException("Unable to locate ${BOC.name} in Network Map Service")
|
||||
val notaryLegalIdentity = rpc.partyFromX500Name(DUMMY_NOTARY.name)
|
||||
?: throw IllegalStateException("Unable to locate ${DUMMY_NOTARY.name} in Network Map Service")
|
||||
val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity)
|
||||
?: throw IllegalStateException("Unable to locate notary node in network map cache")
|
||||
val me = rpc.nodeIdentity()
|
||||
val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random())
|
||||
// issuer random amounts of currency totaling 30000.DOLLARS in parallel
|
||||
val resultFutures = amounts.map { pennies ->
|
||||
rpc.startFlow(::IssuanceRequester, Amount(pennies, amount.token), me.legalIdentity, OpaqueBytes.of(1), bankOfCordaParty, anonymous).returnValue
|
||||
rpc.startFlow(::IssuanceRequester, Amount(pennies, amount.token), me.legalIdentity, OpaqueBytes.of(1), bankOfCordaParty, notaryNode.notaryIdentity, anonymous).returnValue
|
||||
}
|
||||
|
||||
Futures.allAsList(resultFutures).getOrThrow()
|
||||
|
@ -11,6 +11,7 @@ import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.seconds
|
||||
@ -50,7 +51,7 @@ class SellerFlow(val otherParty: Party,
|
||||
progressTracker.currentStep = SELF_ISSUING
|
||||
|
||||
val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0]
|
||||
val cpOwnerKey = serviceHub.legalIdentityKey
|
||||
val cpOwnerKey = serviceHub.keyManagementService.freshKey()
|
||||
val commercialPaper = selfIssueSomeCommercialPaper(serviceHub.myInfo.legalIdentity, notary)
|
||||
|
||||
progressTracker.currentStep = TRADING
|
||||
@ -62,7 +63,7 @@ class SellerFlow(val otherParty: Party,
|
||||
notary,
|
||||
commercialPaper,
|
||||
amount,
|
||||
cpOwnerKey,
|
||||
AnonymousParty(cpOwnerKey),
|
||||
progressTracker.getChildProgressTracker(TRADING)!!)
|
||||
return subFlow(seller)
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ class NewTransaction : Fragment() {
|
||||
command.recipient,
|
||||
command.issueRef,
|
||||
myIdentity.value!!.legalIdentity,
|
||||
command.notary,
|
||||
command.anonymous)
|
||||
} else {
|
||||
command.startFlow(rpcProxy.value!!)
|
||||
|
Loading…
x
Reference in New Issue
Block a user