mirror of
https://github.com/corda/corda.git
synced 2024-12-22 06:17:55 +00:00
Remove dummy cash issuer from two party trade flows
Switch to using a dedicated bank node in two party trade flows so that when KYC checks are enforced, all nodes on the network know who the issuer is.
This commit is contained in:
parent
070f0c040e
commit
9d3ad5fe06
@ -2,27 +2,33 @@ package net.corda.node.messaging
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.contracts.CommercialPaper
|
||||
import net.corda.contracts.asset.*
|
||||
import net.corda.core.*
|
||||
import net.corda.contracts.asset.CASH
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.contracts.asset.`issued by`
|
||||
import net.corda.contracts.asset.`owned by`
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.AnonymousPartyAndPath
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.map
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.rootCause
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
@ -51,7 +57,6 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.concurrent.Future
|
||||
import java.util.jar.JarOutputStream
|
||||
@ -89,10 +94,12 @@ class TwoPartyTradeFlowTests {
|
||||
mockNet = MockNetwork(false, true)
|
||||
|
||||
ledger {
|
||||
val basketOfNodes = mockNet.createSomeNodes(2)
|
||||
val basketOfNodes = mockNet.createSomeNodes(3)
|
||||
val notaryNode = basketOfNodes.notaryNode
|
||||
val aliceNode = basketOfNodes.partyNodes[0]
|
||||
val bobNode = basketOfNodes.partyNodes[1]
|
||||
val bankNode = basketOfNodes.partyNodes[2]
|
||||
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
aliceNode.disableDBCloseOnStop()
|
||||
bobNode.disableDBCloseOnStop()
|
||||
@ -102,11 +109,11 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||
fillUpForSeller(false, issuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
|
||||
val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode,
|
||||
"alice's paper".outputStateAndRef())
|
||||
@ -137,6 +144,8 @@ class TwoPartyTradeFlowTests {
|
||||
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
|
||||
val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name)
|
||||
val cpIssuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
aliceNode.disableDBCloseOnStop()
|
||||
bobNode.disableDBCloseOnStop()
|
||||
@ -146,11 +155,11 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||
fillUpForSeller(false, cpIssuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
|
||||
val cashLockId = UUID.randomUUID()
|
||||
bobNode.database.transaction {
|
||||
@ -186,6 +195,8 @@ class TwoPartyTradeFlowTests {
|
||||
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
|
||||
var bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
|
||||
val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name)
|
||||
val cpIssuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
aliceNode.services.identityService.registerIdentity(bobNode.info.legalIdentityAndCert)
|
||||
bobNode.services.identityService.registerIdentity(aliceNode.info.legalIdentityAndCert)
|
||||
@ -201,10 +212,10 @@ class TwoPartyTradeFlowTests {
|
||||
bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity)
|
||||
}
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||
fillUpForSeller(false, cpIssuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
val aliceFuture = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult
|
||||
|
||||
// Everything is on this thread so we can now step through the flow one step at a time.
|
||||
@ -305,6 +316,8 @@ class TwoPartyTradeFlowTests {
|
||||
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
|
||||
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
|
||||
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
|
||||
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
ledger(aliceNode.services) {
|
||||
|
||||
@ -319,16 +332,14 @@ class TwoPartyTradeFlowTests {
|
||||
attachment(ByteArrayInputStream(stream.toByteArray()))
|
||||
}
|
||||
|
||||
val extraKey = bobNode.services.keyManagementService.keys.single()
|
||||
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraKey),
|
||||
DUMMY_CASH_ISSUER.party,
|
||||
val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.legalIdentity.owningKey),
|
||||
notaryNode.info.notaryIdentity).second
|
||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, extraKey, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
|
||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode)
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||
fillUpForSeller(false, issuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), attachmentID, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
|
||||
mockNet.runNetwork() // Clear network map registration messages
|
||||
|
||||
@ -404,6 +415,8 @@ class TwoPartyTradeFlowTests {
|
||||
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name)
|
||||
val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name)
|
||||
val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name)
|
||||
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
ledger(aliceNode.services) {
|
||||
|
||||
@ -419,17 +432,16 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
|
||||
val bobsKey = bobNode.services.keyManagementService.keys.single()
|
||||
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey),
|
||||
DUMMY_CASH_ISSUER.party,
|
||||
val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobsKey),
|
||||
notaryNode.info.notaryIdentity).second
|
||||
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode)
|
||||
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||
fillUpForSeller(false, issuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), attachmentID, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
|
||||
mockNet.runNetwork() // Clear network map registration messages
|
||||
|
||||
@ -503,7 +515,7 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
val buyerFlows: Observable<BuyerAcceptor> = buyerNode.registerInitiatedFlow(BuyerAcceptor::class.java)
|
||||
val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine }
|
||||
val seller = SellerInitiator(buyerNode.info.legalIdentity, notaryNode.info, assetToSell, 1000.DOLLARS, anonymousSeller.party)
|
||||
val seller = SellerInitiator(buyerNode.info.legalIdentity, notaryNode.info, assetToSell, 1000.DOLLARS, anonymousSeller)
|
||||
val sellerResult = sellerNode.services.startFlow(seller).resultFuture
|
||||
return RunResult(firstBuyerFiber, sellerResult, seller.stateMachine.id)
|
||||
}
|
||||
@ -513,7 +525,7 @@ class TwoPartyTradeFlowTests {
|
||||
val notary: NodeInfo,
|
||||
val assetToSell: StateAndRef<OwnableState>,
|
||||
val price: Amount<Currency>,
|
||||
val me: AbstractParty) : FlowLogic<SignedTransaction>() {
|
||||
val me: AnonymousPartyAndPath) : FlowLogic<SignedTransaction>() {
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
send(buyer, Pair(notary.notaryIdentity, price))
|
||||
@ -522,7 +534,7 @@ class TwoPartyTradeFlowTests {
|
||||
notary,
|
||||
assetToSell,
|
||||
price,
|
||||
me))
|
||||
me.party))
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,19 +558,20 @@ class TwoPartyTradeFlowTests {
|
||||
val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||
val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name)
|
||||
val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name)
|
||||
val issuer = MEGA_CORP.ref(1, 2, 3)
|
||||
val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name)
|
||||
val issuer = bankNode.info.legalIdentity.ref(1, 2, 3)
|
||||
|
||||
val bobsBadCash = bobNode.database.transaction {
|
||||
fillUpForBuyer(bobError, bobNode.info.legalIdentity, DUMMY_CASH_ISSUER.party,
|
||||
fillUpForBuyer(bobError, issuer, bobNode.info.legalIdentity,
|
||||
notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(aliceError, aliceNode.info.legalIdentity,
|
||||
fillUpForSeller(aliceError, issuer, aliceNode.info.legalIdentity,
|
||||
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
|
||||
}
|
||||
|
||||
insertFakeTransactions(bobsBadCash, bobNode, notaryNode, DUMMY_CASH_ISSUER_KEY.public, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, MEGA_CORP_PUBKEY)
|
||||
insertFakeTransactions(bobsBadCash, bobNode, notaryNode, bankNode)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode)
|
||||
|
||||
mockNet.runNetwork() // Clear network map registration messages
|
||||
|
||||
@ -582,8 +595,8 @@ class TwoPartyTradeFlowTests {
|
||||
private fun insertFakeTransactions(
|
||||
wtxToSign: List<WireTransaction>,
|
||||
node: AbstractNode,
|
||||
notaryNode: MockNetwork.MockNode,
|
||||
vararg extraKeys: PublicKey): Map<SecureHash, SignedTransaction> {
|
||||
notaryNode: AbstractNode,
|
||||
vararg extraSigningNodes: AbstractNode): Map<SecureHash, SignedTransaction> {
|
||||
|
||||
val signed = wtxToSign.map {
|
||||
val bits = it.serialize()
|
||||
@ -591,14 +604,8 @@ class TwoPartyTradeFlowTests {
|
||||
val sigs = mutableListOf<DigitalSignature.WithKey>()
|
||||
sigs.add(node.services.keyManagementService.sign(id.bytes, node.services.legalIdentityKey))
|
||||
sigs.add(notaryNode.services.keyManagementService.sign(id.bytes, notaryNode.services.notaryIdentityKey))
|
||||
for (extraKey in extraKeys) {
|
||||
if (extraKey == DUMMY_CASH_ISSUER_KEY.public) {
|
||||
sigs.add(DUMMY_CASH_ISSUER_KEY.sign(id.bytes))
|
||||
} else if (extraKey == MEGA_CORP_PUBKEY) {
|
||||
sigs.add(MEGA_CORP_KEY.sign(id.bytes))
|
||||
} else {
|
||||
sigs.add(node.services.keyManagementService.sign(id.bytes, extraKey))
|
||||
}
|
||||
extraSigningNodes.forEach { currentNode ->
|
||||
sigs.add(currentNode.services.keyManagementService.sign(id.bytes, currentNode.info.legalIdentity.owningKey))
|
||||
}
|
||||
SignedTransaction(bits, sigs)
|
||||
}
|
||||
@ -614,10 +621,10 @@ class TwoPartyTradeFlowTests {
|
||||
|
||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
||||
withError: Boolean,
|
||||
issuer: PartyAndReference,
|
||||
owner: AbstractParty,
|
||||
issuer: AbstractParty,
|
||||
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
||||
val interimOwner = MEGA_CORP
|
||||
val interimOwner = issuer.party
|
||||
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
||||
// wants to sell to Bob.
|
||||
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
@ -625,10 +632,10 @@ class TwoPartyTradeFlowTests {
|
||||
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
|
||||
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
|
||||
if (!withError) {
|
||||
command(issuer.owningKey) { Cash.Commands.Issue() }
|
||||
command(issuer.party.owningKey) { Cash.Commands.Issue() }
|
||||
} else {
|
||||
// Put a broken command on so at least a signature is created
|
||||
command(issuer.owningKey) { Cash.Commands.Move() }
|
||||
command(issuer.party.owningKey) { Cash.Commands.Move() }
|
||||
}
|
||||
timeWindow(TEST_TX_TIME)
|
||||
if (withError) {
|
||||
@ -660,15 +667,16 @@ class TwoPartyTradeFlowTests {
|
||||
|
||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
||||
withError: Boolean,
|
||||
issuer: PartyAndReference,
|
||||
owner: AbstractParty,
|
||||
amount: Amount<Issued<Currency>>,
|
||||
attachmentID: SecureHash?,
|
||||
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
||||
val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||
output("alice's paper", notary = notary) {
|
||||
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), owner, amount, TEST_TX_TIME + 7.days)
|
||||
CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)
|
||||
}
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() }
|
||||
if (!withError)
|
||||
timeWindow(time = TEST_TX_TIME)
|
||||
if (attachmentID != null)
|
||||
|
Loading…
Reference in New Issue
Block a user