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:
Ross Nicoll 2017-07-19 16:00:14 +01:00 committed by GitHub
parent 070f0c040e
commit 9d3ad5fe06

View File

@ -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)