Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Использование технологии Блокчейн в финтех-индустрии

5206c19df417b8876825b5561344c1a0?s=47 Exactpro
PRO
November 16, 2018

Использование технологии Блокчейн в финтех-индустрии

Антон Ситников

Главный архитектор ПО, Exactpro

TMPA School: https://school.tmpaconf.org/
Exactpro: https://exactpro.com/

5206c19df417b8876825b5561344c1a0?s=128

Exactpro
PRO

November 16, 2018
Tweet

Transcript

  1. Build Software to Test Software exactpro.com Технология blockchain в FinTech

    Ноябрь 2018 Антон Ситников
  2. 2 Build Software to Test Software exactpro.com • Компания Exactpro

    была основана в день тестировщика 09.09.2009 года и специализируется на разработке и тестировании программного обеспечения для ведущих финансовых организаций, к числу которых относятся биржи, инвестиционные банки, брокеры и поставщики технологических решений. • Exactpro была частью группы компаний Лондонская фондовая биржа с 2015, снова став независимой в январе 2018 года в результате выкупа компании менеджментом. • Основанная в 2009 году небольшой командой из 10 человек, Exactpro, спустя годы, выросла до компании со штатом более 550 человек и проектами по тестированию и разработке программного обеспечения во многих странах мира. • Наши проекты - это анализ и верификация распределенных и высоконагруженных технологических платформ, систем клиринга и взаиморасчетов и систем риск-менеджмента, основанных на ультрасовременных технологиях, с целью достижения их бесперебойной работы в условиях промышленной эксплуатации. Build Software to Test Software
  3. 3 Build Software to Test Software exactpro.com Исходный код https://bit.ly/2SWmnHw

    https://github.com/ASitnikov/corda-ccp-example
  4. 4 Build Software to Test Software exactpro.com Импорт проекта в

    IntelliJ IDEA
  5. 5 Build Software to Test Software exactpro.com Импорт проекта в

    IntelliJ IDEA
  6. 6 Build Software to Test Software exactpro.com Альянсы DLT

  7. 7 Build Software to Test Software exactpro.com Corda • Сеть

    Corda – это аутентифицированная пиринговая сеть узлов, каждый узел которой представляет собой JVM с сервисами Corda и выполняет приложения, известные как CordApp • Узлы взаимодействуют друг с другом напрямую через AMQP/1.0, используя зашифрованные по технологии TLS сообщения. Это означает, что данные распространяются только между теми узлами, которым они нужны; в Corda нет возможности рассылать сообщения всем узлам сети одновременно (broadcast) • Сети Corda - частично приватные. В каждой сети есть специальный сервис-дворецкий (doorman), который определят правила о том, какую информацию должны предоставить узлы, и какие шаги они должны выполнить, чтобы быть допущенными в сеть.
  8. 8 Build Software to Test Software exactpro.com Реестр (Ledger) 1

    6 7 5 2 3 4 9 8
  9. 9 Build Software to Test Software exactpro.com Сеть и идентификация

    • Подлинность узлов подтверждается сертификатами X.509 • Идентификаторы (сертификаты) общеизвестных узлов публикуются в карте сети • Конфиденциальные идентификаторы предоставляются только тем узлам, которым они необходимы
  10. 10 Build Software to Test Software exactpro.com Состояние (State) CASH

    CONTRACT REF PARTICIPANTS Alice Bob CASH STATE PROPERTIES OWNER: AMOUNT: CURRENCY: Alice 10 USD
  11. 11 Build Software to Test Software exactpro.com Цепочка состояний NO

    CASH USD 100 USD 50 2017-02-01 timeline 2017-02-24 ИСТОРИЯ
  12. 12 Build Software to Test Software exactpro.com Хранилище (Vault) B

    HEAD C HEAD A HEAD S HEAD A0 ИСТОРИЯ ПОГАШЕННЫЕ ИСТОРИЧЕСКИЕ СОСТОЯНИЯ НЕПОГАШЕННЫЕ A1 ИСТОРИЯ A2 A3 B0 ИСТОРИЯ B1 ИСТОРИЯ ИСТОРИЯ ИСТОРИЯ S0 ИСТОРИЯ S1 S2 ИСТОРИЯ ИСТОРИЯ
  13. 13 Build Software to Test Software exactpro.com Контракт Контракт проверяет

    достоверность транзакции CASH 0 CASH CONTRACT CASH 1 BOND 1 BOND 2 BOND CONTRACT ССЫЛАЕТСЯ ДОСТОВЕРЕН? ССЫЛАЕТСЯ ССЫЛАЕТСЯ ССЫЛАЕТСЯ ДОСТОВЕРЕН?
  14. 14 Build Software to Test Software exactpro.com Транзакция • Идентифицируется

    хешем • Атомарная • Несколько входных состояний • Несколько выходных состояний • Хотя бы одна команда CASH 0 CASH 1 BOND 1 BOND 2
  15. 15 Build Software to Test Software exactpro.com Команда 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 COUPONE (ALICE 5%) PAY (BOB 5 USD) ВХОДНЫЕ СОСТОЯНИЯ КОМАНДЫ ВЫХОДНЫЕ СОСТОЯНИЯ SIG Alice SIG Bob
  16. 16 Build Software to Test Software exactpro.com Цепочка транзакций TxHash:

    f70f5… Index: 0 New State 1 TxHash: 8031d… Index: 1 New State 2 ТРАНЗАКЦИЯ Hash = 95fd065cb0... TxHash: 95fd0… Index: 0 New State 1 TxHash: 0dbfc… Index: 1 New State 2 ТРАНЗАКЦИЯ Hash = 1ba0ce7a71... TxHash: 56005… Index: 0 New State 1 TxHash: 047dd… Index: 1 New State 2 ТРАНЗАКЦИЯ Hash = 0dbfcf6665...
  17. 17 Build Software to Test Software exactpro.com Проведение (committing) транзакции

    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 ИСТОРИЯ ИСТОРИЯ
  18. 18 Build Software to Test Software exactpro.com Достоверность (validity) транзакции

    • Достоверность транзакции: для предложенной транзакции, ровно как и для каждой транзакции в цепочках транзакций, восходящих ко входным состояниям предложенной транзакции: ▪ Транзакция имеет цифровые подписи всех требуемых сторон ▪ Транзакция не нарушает требования контракта • Уникальность транзакции: не существует других проведенных транзакций, которые погасили (consume) любое из входных состояний предложенной транзакции
  19. 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
  20. 20 Build Software to Test Software exactpro.com Нотариальный узел (notary)

    • Нотариальный узел подтверждает уникальность транзакции, предотвращая двойные расходы • Нотариальные узлы могут опционально удостоверять транзакции • В сети может существовать несколько нотариальных узлов
  21. 21 Build Software to Test Software exactpro.com Оракул (oracle) •

    Оракул – это сетевой сервис, предоставляющий по запросу информацию о каком-либо факте и подтверждающий ее • Оракул может предоставить факт в виде команды или в виде вложения (attachment) • Оракул подписывает только достоверные данные
  22. 22 Build Software to Test Software exactpro.com Хеш-деревья (Merkle tree)

    Корень дерева хешей H(5+6) H(1+2) H(3+4) H(Н(g)+0) H(Н(e)+H(f)) H(a) H(b) H(c) H(d) H(e) H(f) H(g) 0 H(Н(c)+H(d)) H(Н(a)+H(b)) входы выходы вложение команда и время нулевой хеш остальное 1 2 5 3 4 6
  23. 23 Build Software to Test Software exactpro.com Хеш-деревья (Merkle tree)

    Корень дерева хешей H(1+6) H(4+6) H(Н(g)+3) H(2+H(f)) H(f) H(g) команда и время нулевой хеш остальное 2 1 4 5 6 3 предоставлен предоставлен
  24. 24 Build Software to Test Software exactpro.com Клиринг

  25. 25 Build Software to Test Software exactpro.com Исходный код https://bit.ly/2SWmnHw

    https://github.com/ASitnikov/corda-ccp-example
  26. 26 Build Software to Test Software exactpro.com Импорт проекта в

    IntelliJ IDEA
  27. 27 Build Software to Test Software exactpro.com Импорт проекта в

    IntelliJ IDEA
  28. 28 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
  29. 29 Build Software to Test Software exactpro.com Interface ContractState @CordaSerializable

    interface ContractState { val participants: List<AbstractParty> } 014 015 026 027 Corda override val participants: List<Party> = listOfNotNull(buyerOrSeller, ccp) 024 main/.../contract/TradeContract.kt
  30. 30 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
  31. 31 Build Software to Test Software exactpro.com Interface QueryableState interface

    QueryableState : ContractState { fun supportedSchemas(): Iterable<MappedSchema> fun generateMappedObject(schema: MappedSchema): PersistentState } 018 022 023 027 028 Corda
  32. 32 Build Software to Test Software exactpro.com Class State override

    fun supportedSchemas(): Iterable<MappedSchema> = 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
  33. 33 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
  34. 34 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
  35. 35 Build Software to Test Software exactpro.com Class TradeContract override

    fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand<Commands>() 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
  36. 36 Build Software to Test Software exactpro.com Class RegisterTradeFlow @InitiatingFlow

    @StartableByRPC class RegisterTradeFlow(val fixMessage: FixMessage, val ccp: Party) : FlowLogic<SignedTransaction>() { @Suspendable override fun call(): SignedTransaction { 014 015 016 017 018 048 049 main/.../flow/RegisterTradeFlow.kt
  37. 37 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
  38. 38 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
  39. 39 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
  40. 40 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
  41. 41 Build Software to Test Software exactpro.com Class AcceptTradeFlow @InitiatedBy(RegisterTradeFlow::class)

    class AcceptTradeFlow(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() { @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
  42. 42 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
  43. 43 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
  44. 44 Build Software to Test Software exactpro.com Class ClearTradeFlow @InitiatingFlow

    @StartableByRPC class ClearTradeFlow(val tradeId: String, val ccp: Party) : FlowLogic<Void?>() { @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
  45. 45 Build Software to Test Software exactpro.com Class ClearTradeHandler @InitiatedBy(ClearTradeFlow::class)

    class ClearTradeHandler(val otherPartyFlow: FlowSession) : FlowLogic<Void?>() { @Suspendable override fun call(): Void? { val tradeId = otherPartyFlow.receive<ClearTradeFlow.ClearTradeRequest>() .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<TradeContract.State>(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
  46. 46 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
  47. 47 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
  48. 48 Build Software to Test Software exactpro.com Class TradeContract override

    fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand<Commands>() 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
  49. 49 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
  50. 50 Build Software to Test Software exactpro.com