Slide 1

Slide 1 text

Build Software to Test Software exactpro.com Blockchain technology in FinTech Nov, 08 2018 Anton Sitnikov

Slide 2

Slide 2 text

2 Build Software to Test Software exactpro.com EXACTPRO Exactpro • A specialist firm focused on functional and non functional testing of exchanges, clearing houses, depositories and other market infrastructures • Incorporated in 2009 with 10 people, our company has experienced significant growth as satisfied clients require more services; now employing 550 specialists. • Part of London Stock Exchange Group (LSEG) from May 2015 till January 2018. Exactpro management buyout from LSEG in January 2018. Headquartered in UK, with operations in US, Georgia and Russia. • We provide software testing services for mission critical technology that underpins global financial markets. Our clients are regulated by FCA, Bank of England and their counterparts from other countries.

Slide 3

Slide 3 text

3 Build Software to Test Software exactpro.com Source code https://bit.ly/2SWmnHw https://github.com/ASitnikov/corda-ccp-example

Slide 4

Slide 4 text

4 Build Software to Test Software exactpro.com Importing project to IntelliJ IDEA

Slide 5

Slide 5 text

5 Build Software to Test Software exactpro.com Importing project to IntelliJ IDEA

Slide 6

Slide 6 text

6 Build Software to Test Software exactpro.com DLT alliances

Slide 7

Slide 7 text

7 Build Software to Test Software exactpro.com Corda ● A Corda network is an authenticated peer-to-peer network of nodes, where each node is a JVM run-time environment hosting Corda services and executing applications known as CorDapps. ● All communication between nodes is direct, with TLS-encrypted messages sent over AMQP/1.0. This means that data is shared only on a need-to-know basis; in Corda, there are no global broadcasts. ● Corda networks are semi-private. Each network has a doorman service that enforces rules regarding the information that nodes must provide and the know-your-customer processes that they must complete before being admitted to the network.

Slide 8

Slide 8 text

8 Build Software to Test Software exactpro.com Ledger 1 6 7 5 2 3 4 9 8

Slide 9

Slide 9 text

9 Build Software to Test Software exactpro.com Network and Indentity • Identities are attested to by X.509 certificate • Well known identities are published in the network map • Confidential identities are only shared on a need to know basis

Slide 10

Slide 10 text

10 Build Software to Test Software exactpro.com State CASH CONTRACT REF PARTICIPANTS Alice Bob CASH STATE PROPERTIES OWNER: AMOUNT: CURRENCY: Alice 10 USD

Slide 11

Slide 11 text

11 Build Software to Test Software exactpro.com State sequence NO CASH USD 100 USD 50 2017-02-01 timeline 2017-02-24 HISTORIC

Slide 12

Slide 12 text

12 Build Software to Test Software exactpro.com Vault B HEAD C HEAD A HEAD S HEAD B1 A3 S2 B0 A2 S1 A1 S0 A0 HISTORIC CONSUMED HISTORICAL STATES UNCONSUMED HISTORIC HISTORIC HISTORIC HISTORIC HISTORIC HISTORIC HISTORIC HISTORIC

Slide 13

Slide 13 text

13 Build Software to Test Software exactpro.com Contract Contract checks transaction validity CASH 0 CASH CONTRACT CASH 1 BOND 1 BOND 2 BOND CONTRACT REFERENCES VALIDATES REFERENCES REFERENCES REFERENCES VALIDATES

Slide 14

Slide 14 text

14 Build Software to Test Software exactpro.com Command BOND 1 OWNER: Alice VALUE: 100 USD COUPON PAID: NO BOND 2 OWNER: Alice VALUE: 100 USD COUPON PAID: YES CASH 1 OWNER: Bob AMOUNT: 5 USD CASH 2 OWNER: Alice AMOUNT: 5 USD HISTORIC HISTORIC COUPONE (ALICE 5%) PAY (BOB 5 USD) INPUT STATES COMMANDS OUTPUT STATES SIG Alice SIG Bob

Slide 15

Slide 15 text

15 Build Software to Test Software exactpro.com Transaction • Is identified by a hash • Is atomic • Can have multiple input states • Can have multiple output states • Should have at least one command CASH 0 CASH 1 BOND 1 BOND 2 TRANSACTION

Slide 16

Slide 16 text

16 Build Software to Test Software exactpro.com Transaction chain TxHash: f70f5… Index: 0 New State 1 TxHash: 8031d… Index: 1 New State 2 TRANSACTION Hash = 95fd065cb0... TxHash: 95fd0… Index: 0 New State 1 TxHash: 0dbfc… Index: 1 New State 2 TRANSACTION Hash = 1ba0ce7a71... TxHash: 56005… Index: 0 New State 1 TxHash: 047dd… Index: 1 New State 2 TRANSACTION Hash = 0dbfcf6665...

Slide 17

Slide 17 text

17 Build Software to Test Software exactpro.com Committing transaction SIG Alice SIG Bob BOND 1 OWNER: Alice VALUE: 5 USD COUPON PAID: NO BOND 2 OWNER: Alice VALUE: 5 USD COUPON PAID: YES CASH 1 OWNER: Bob AMOUNT: 5 USD CASH 2 OWNER: Alice AMOUNT: 5 USD HISTORIC HISTORIC

Slide 18

Slide 18 text

18 Build Software to Test Software exactpro.com Transaction validity • Transaction validity: For both the proposed transaction, and every transaction in the chain of transactions that created the current proposed transaction’s inputs: ▪ The transaction is digitally signed by all the required parties ▪ The transaction is contractually valid • Transaction uniqueness: There exists no other committed transaction that has consumed any of the inputs to our proposed transaction

Slide 19

Slide 19 text

19 Build Software to Test Software exactpro.com Flow INITIATOR (ALICE) GET DATA FROM INTERNAL SYSTEM CREATE TX SIGN TX RESPONDER (BOB) INSPECT AND VERIFY TX SIGN TX COMMIT TX END INSPECT AND VERIFY TX COMMIT TX END SEND (TX+SIG) SEND (TX+SIG) FLOW SUSPENDED AND CHECKPOINTED

Slide 20

Slide 20 text

20 Build Software to Test Software exactpro.com Notary • Notaries provide uniqueness consensus which prevents “double-spends” • Notaries may optionally also validate transactions • A network can have several notaries, each running a different consensus algorithm

Slide 21

Slide 21 text

21 Build Software to Test Software exactpro.com Clearing

Slide 22

Slide 22 text

22 Build Software to Test Software exactpro.com Source code https://bit.ly/2SWmnHw https://github.com/ASitnikov/corda-ccp-example

Slide 23

Slide 23 text

23 Build Software to Test Software exactpro.com Importing project to IntelliJ IDEA

Slide 24

Slide 24 text

24 Build Software to Test Software exactpro.com Importing project to IntelliJ IDEA

Slide 25

Slide 25 text

25 Build Software to Test Software exactpro.com Class State data class State( val tradeId: String, val symbol: String, val side: Side, val price: Double, val size: Double, val buyerOrSeller: Party, val ccp: Party, val cleared: Boolean) : ContractState, QueryableState { 014 015 016 017 018 019 020 021 022 main/.../contract/TradeContract.kt

Slide 26

Slide 26 text

26 Build Software to Test Software exactpro.com Interface ContractState @CordaSerializable interface ContractState { val participants: List } 014 015 026 027 Corda override val participants: List = listOfNotNull(buyerOrSeller, ccp) 024 main/.../contract/TradeContract.kt

Slide 27

Slide 27 text

27 Build Software to Test Software exactpro.com Object TradeSchemaV1 object TradeSchema object TradeSchemaV1 : MappedSchema(schemaFamily = TradeSchema.javaClass, version = 1, mappedTypes = listOf(PersistentTradeState::class.java)) { @Entity @Table(name = "trade_states") class PersistentTradeState( @Column(name = "trade_id") var tradeId: String, @Column(name = "symbol") var symbol: String, @Column(name = "side") var side: Side, @Column(name = "price") var price: Double, @Column(name = "size") var size: Double, @Column(name = "cleared") var cleared: Boolean ) : PersistentState() { 011 012 013 014 015 016 017 018 019 020 022 023 025 026 028 029 031 032 040 041 042 main/.../schema/TradeSchema.kt

Slide 28

Slide 28 text

28 Build Software to Test Software exactpro.com Interface QueryableState interface QueryableState : ContractState { fun supportedSchemas(): Iterable fun generateMappedObject(schema: MappedSchema): PersistentState } 018 022 023 027 028 Corda

Slide 29

Slide 29 text

29 Build Software to Test Software exactpro.com Class State override fun supportedSchemas(): Iterable = listOf(TradeSchemaV1) override fun generateMappedObject(schema: MappedSchema): PersistentState { return when (schema) { is TradeSchemaV1 -> TradeSchemaV1.PersistentTradeState( tradeId = this.tradeId, symbol = this.symbol, side = this.side, price = this.price, size = this.size, buyerOrSeller = this.buyerOrSeller, ccp = this.ccp, cleared = this.cleared ) else -> throw IllegalArgumentException("Unrecognised schema $schema") } } 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 main/.../contract/TradeContract.kt

Slide 30

Slide 30 text

30 Build Software to Test Software exactpro.com Interface Contract @CordaSerializable interface Contract { @Throws(IllegalArgumentException::class) fun verify(tx: LedgerTransaction) } 232 233 240 241 242 Corda

Slide 31

Slide 31 text

31 Build Software to Test Software exactpro.com Class TradeContract class TradeContract : Contract { companion object { val ID = "com.exactpro.example.contract.TradeContract" } sealed class Commands : TypeOnlyCommandData() { class RegisterTrade : Commands() class ClearTrade : Commands() } } 012 013 046 047 048 049 085 086 087 088 089 main/.../contract/TradeContract.kt

Slide 32

Slide 32 text

32 Build Software to Test Software exactpro.com Class TradeContract override fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand() when (command.value) { is Commands.RegisterTrade -> requireThat { "there is no input states" using (tx.inputStates.isEmpty()) val output = tx.outputs.single().data "This must be an TradeState state" using (output is TradeContract.State) val tradeOutput = output as TradeContract.State "trade is not cleared" using (!tradeOutput.cleared) "size is positive" using (tradeOutput.size > 0) "price is positive" using (tradeOutput.price > 0) } is Commands.ClearTrade -> requireThat { 052 053 054 055 056 057 058 059 060 061 062 063 064 main/.../contract/TradeContract.kt

Slide 33

Slide 33 text

33 Build Software to Test Software exactpro.com Class RegisterTradeFlow @InitiatingFlow @StartableByRPC class RegisterTradeFlow(val fixMessage: FixMessage, val ccp: Party) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { 014 015 016 017 018 048 049 main/.../flow/RegisterTradeFlow.kt

Slide 34

Slide 34 text

34 Build Software to Test Software exactpro.com Fun RegisterTradeFlow::call progressTracker.currentStep = GENERATING_TRANSACTION val notary = serviceHub.networkMapCache.notaryIdentities.first() val tradeState = TradeContract.State( tradeId = fixMessage.tradeId, symbol = fixMessage.symbol, side = fixMessage.side, price = fixMessage.price, size = fixMessage.size, buyerOrSeller = serviceHub.myInfo.legalIdentities.first(), ccp = ccp, cleared = false) val txCommand = Command( TradeContract.Commands.RegisterTrade(), listOf(ourIdentity.owningKey, ccp.owningKey)) val txBuilder = TransactionBuilder(notary) .addOutputState(tradeState, TradeContract.ID) .addCommand(txCommand) 052 053 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 main/.../flow/RegisterTradeFlow.kt

Slide 35

Slide 35 text

35 Build Software to Test Software exactpro.com Class RegisterTradeFlow companion object { object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction.") object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.") object SIGNING_TRANSACTION : ProgressTracker.Step("Signing transaction with our private key.") object GATHERING_SIGS : ProgressTracker.Step("Getting the CCP signature.") { override fun childProgressTracker() = CollectSignaturesFlow.tracker() } object FINALISING_TRANSACTION : ProgressTracker.Step("Obtaining notary signature and recording transaction.") { override fun childProgressTracker() = FinalityFlow.tracker() } 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 main/.../flow/RegisterTradeFlow.kt

Slide 36

Slide 36 text

36 Build Software to Test Software exactpro.com Class RegisterTradeFlow companion object { fun tracker() = ProgressTracker( GENERATING_TRANSACTION, VERIFYING_TRANSACTION, SIGNING_TRANSACTION, GATHERING_SIGS, FINALISING_TRANSACTION ) } 019 037 038 039 040 041 042 043 044 main/.../flow/RegisterTradeFlow.kt

Slide 37

Slide 37 text

37 Build Software to Test Software exactpro.com Fun RegisterTradeFlow::call progressTracker.currentStep = VERIFYING_TRANSACTION txBuilder.verify(serviceHub) progressTracker.currentStep = SIGNING_TRANSACTION val ptx = serviceHub.signInitialTransaction(txBuilder) progressTracker.currentStep = GATHERING_SIGS val ccpSession = initiateFlow(ccp) val stx = subFlow(CollectSignaturesFlow( ptx, listOf(ccpSession), GATHERING_SIGS.childProgressTracker())) progressTracker.currentStep = FINALISING_TRANSACTION return subFlow(FinalityFlow(stx, FINALISING_TRANSACTION.childProgressTracker())) } 076 077 078 081 082 083 086 087 088 089 090 093 094 095 main/.../flow/RegisterTradeFlow.kt

Slide 38

Slide 38 text

38 Build Software to Test Software exactpro.com Class AcceptTradeFlow @InitiatedBy(RegisterTradeFlow::class) class AcceptTradeFlow(val otherPartyFlow: FlowSession) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) { override fun checkTransaction(stx: SignedTransaction) = requireThat { val output = stx.tx.outputs.single().data "This must be an TradeState transaction." using (output is TradeContract.State) } } return subFlow(signTransactionFlow) } } 098 099 100 101 102 103 104 105 106 107 108 109 110 main/.../flow/RegisterTradeFlow.kt

Slide 39

Slide 39 text

39 Build Software to Test Software exactpro.com Class RegisterTradeFlowTests class RegisterTradeFlowTests { @Before fun start() { val cordappPackages = listOf( "com.exactpro.example.contract", "com.exactpro.example.flow", "com.exactpro.example.schema") mockNet = MockNetwork( servicePeerAllocationStrategy = ServicePeerAllocationStrategy.RoundRobin(), cordappPackages = cordappPackages) partyANode = mockNet.createPartyNode(PARTY_A_NAME) partyA = partyANode.info.identityFromX500Name(PARTY_A_NAME) ccpNode = mockNet.createPartyNode(CCP_NAME) ccp = ccpNode.info.identityFromX500Name(CCP_NAME) notary = mockNet.defaultNotaryIdentity } @After fun cleanUp() { mockNet.stopNodes() } 016 017 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 test/.../flow/RegisterTradeFlowTests.kt

Slide 40

Slide 40 text

40 Build Software to Test Software exactpro.com Class RegisterTradeFlowTests class RegisterTradeFlowTests { @Test fun `register a trade`() { val fixMessage = FixMessage( tradeId = "1", symbol = "MSFT", side = Side.BUY, price = 2.0, size = 100.0) val future = partyANode.startFlow(RegisterTradeFlow(fixMessage, ccp)) mockNet.runNetwork() val tx = future.getOrThrow() } } 016 017 045 046 047 048 049 050 051 052 053 054 055 056 057 test/.../flow/RegisterTradeFlowTests.kt

Slide 41

Slide 41 text

41 Build Software to Test Software exactpro.com Class ClearTradeFlow @InitiatingFlow @StartableByRPC class ClearTradeFlow(val tradeId: String, val ccp: Party) : FlowLogic() { @Suspendable override fun call(): Void? { val ccpSession = initiateFlow(ccp) ccpSession.send(ClearTradeRequest(tradeId)) return null } @CordaSerializable class ClearTradeRequest(val tradeId: String) } 018 019 020 021 022 023 024 025 026 027 028 029 030 031 main/.../flow/ClearTradeFlow.kt

Slide 42

Slide 42 text

42 Build Software to Test Software exactpro.com Class ClearTradeHandler @InitiatedBy(ClearTradeFlow::class) class ClearTradeHandler(val otherPartyFlow: FlowSession) : FlowLogic() { @Suspendable override fun call(): Void? { val tradeId = otherPartyFlow.receive() .unwrap { data -> data }.tradeId val expression = builder { TradeSchemaV1.PersistentTradeState::tradeId.equal(tradeId) TradeSchemaV1.PersistentTradeState::cleared.equal(false) } val queryCriteria = QueryCriteria.VaultCustomQueryCriteria(expression) val tradeStateAndRefs = serviceHub.vaultService.queryBy(queryCriteria).states if (tradeStateAndRefs.size == 2) { 033 034 035 054 055 056 057 058 059 060 061 062 063 064 065 066 main/.../flow/ClearTradeFlow.kt

Slide 43

Slide 43 text

43 Build Software to Test Software exactpro.com Fun ClearTradeHandler::call() progressTracker.currentStep = GENERATING_TRANSACTION val notary = serviceHub.networkMapCache.notaryIdentities.first() val txCommand = Command( TradeContract.Commands.ClearTrade(), listOf(ourIdentity.owningKey)) val clearedA = tradeStateAndRefs[0].state.data.copy(cleared = true) val clearedB = tradeStateAndRefs[1].state.data.copy(cleared = true) val txBuilder = TransactionBuilder(notary) .addInputState(tradeStateAndRefs[0]) .addInputState(tradeStateAndRefs[1]) .addOutputState(clearedA, TradeContract.ID) .addOutputState(clearedB, TradeContract.ID) .addCommand(txCommand) 070 071 073 074 075 076 077 078 079 080 081 082 083 084 main/.../flow/ClearTradeFlow.kt

Slide 44

Slide 44 text

44 Build Software to Test Software exactpro.com Fun ClearTradeHandler::call() progressTracker.currentStep = VERIFYING_TRANSACTION txBuilder.verify(serviceHub) progressTracker.currentStep = SIGNING_TRANSACTION val ptx = serviceHub.signInitialTransaction(txBuilder) progressTracker.currentStep = FINALISING_TRANSACTION subFlow(FinalityFlow(ptx, FINALISING_TRANSACTION.childProgressTracker())) } return null } 088 089 090 093 094 095 098 099 100 101 102 main/.../flow/ClearTradeFlow.kt

Slide 45

Slide 45 text

45 Build Software to Test Software exactpro.com Class TradeContract override fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand() when (command.value) { is Commands.ClearTrade -> requireThat { "there are 2 input states" using (tx.inputStates.size == 2) "there are 2 output states" using (tx.outputStates.size == 2) val inputA = tx.inputs[0].state.data as TradeContract.State val inputB = tx.inputs[1].state.data as TradeContract.State "input[0] is not cleared" using (!inputA.cleared) "input[1] is not cleared" using (!inputB.cleared) "inputs have same tradeId" using (inputA.tradeId == inputB.tradeId) "inputs have different sides" using (inputA.side != inputB.side) val outputA = tx.outputs[0].data as TradeContract.State val outputB = tx.outputs[1].data as TradeContract.State "output[0] is cleared" using (outputA.cleared) "output[1] is cleared" using (outputB.cleared) "outputs have same tradeId" using (outputA.tradeId == outputB.tradeId) "outputs have different sides" using (outputA.side != outputB.side) } 052 053 054 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 main/.../contract/TradeContract.kt

Slide 46

Slide 46 text

46 Build Software to Test Software exactpro.com Class ClearTradeFlowTests class ClearTradeFlowTests { @Test fun `clear a trade`() { val fixMessageA = FixMessage( // side = Side.BUY, val futureA = partyANode.startFlow(RegisterTradeFlow(fixMessageA, ccp)) val fixMessageB = FixMessage( // side = Side.SELL val futureB = partyBNode.startFlow(RegisterTradeFlow(fixMessageB, ccp)) mockNet.runNetwork() futureA.getOrThrow() futureB.getOrThrow() val future = partyANode.startFlow(ClearTradeFlow("1", ccp)) mockNet.runNetwork() future.getOrThrow() } 021 022 050 051 052 058 059 060 066 067 068 069 070 071 072 073 074 075 076 test/.../flow/ClearTradeFlowTests.kt

Slide 47

Slide 47 text

47 Build Software to Test Software exactpro.com Further reading ● Oracles ● Information hiding (Merkle trees) ● Anonymous identity ● Corda applications

Slide 48

Slide 48 text

48 Build Software to Test Software exactpro.com Thank you!