Blockchain: hokker PoC moatte wy bouwe?

Jo eagen binne bang en jo hannen jeuke!

Yn eardere artikels hawwe wy behannele mei de technologyen wêrop blockchains binne boud (Wat moatte wy bouwe in blockchain?) en gefallen dy't mei har help kinne wurde útfierd (Wêrom moatte wy in saak bouwe?). It is tiid om mei jo hannen te wurkjen! Om pilots en PoC (Proof of Concept) te ymplementearjen, brûk ik leaver de wolken, om't ... se kinne fan oeral yn 'e wrâld tagonklik wurde en, faaks is it net nedich om tiid te fergrieme oan ferfeelsume ynstallaasje fan it miljeu, om't D'r binne foarôf ynstelde konfiguraasjes. Dus, litte wy wat ienfâldich meitsje, bygelyks in netwurk foar it oerdragen fan munten tusken dielnimmers en litte wy it beskieden Bitcoin neame. Hjirfoar sille wy de IBM-wolk en de universele blockchain Hyperledger Fabric brûke. Litte wy earst útfine wêrom Hyperledger Fabric in universele blockchain wurdt neamd?

Blockchain: hokker PoC moatte wy bouwe?

Hyperledger Fabric - in universele blockchain

Yn 't algemien is in universele ynformaasjesysteem:

  • In set fan tsjinners en in software kearn dat fiert saaklike logika;
  • ynterfaces foar ynteraksje mei it systeem;
  • Tools foar registraasje, autentikaasje en autorisaasje fan apparaten / minsken;
  • Databank dy't operasjonele en argyfgegevens opslacht:

Blockchain: hokker PoC moatte wy bouwe?

De offisjele ferzje fan wat Hyperledger Fabric is kin lêzen wurde op side, en koartsein, Hyperledger Fabric is in iepenboarne platfoarm wêrmei jo privee blockchains kinne bouwe en willekeurige tûke kontrakten útfiere skreaun yn 'e JS en Go-programmearringstalen. Litte wy yn detail sjen nei de arsjitektuer fan Hyperledger Fabric en soargje derfoar dat dit in universele systeem is dat allinich spesifikaasjes hat foar it opslaan en opnimmen fan gegevens. De spesifisiteit is dat de gegevens, lykas yn alle blockchains, opslein wurde yn blokken dy't allinich op 'e blockchain pleatst wurde as de dielnimmers in konsensus berikke en nei it opnimmen fan de gegevens kinne net rêstich korrizjearre of wiske wurde.

Hyperledger Fabric Architecture

It diagram lit de Hyperledger Fabric-arsjitektuer sjen:

Blockchain: hokker PoC moatte wy bouwe?

organisaasjes - organisaasjes befetsje peers, d.w.s. blockchain bestiet fanwege de stipe fan organisaasjes. Ferskillende organisaasjes kinne diel útmeitsje fan itselde kanaal.

Kanaal - in logyske struktuer dy't peers ferieniget yn groepen, d.w.s. de blockchain wurdt oantsjutte. Hyperledger Fabric kin tagelyk meardere blockchains ferwurkje mei ferskate saaklike logika.

Membership Services Provider (MSP) is in CA (Certificate Authority) foar it útjaan fan identiteit en it tawizen fan rollen. Om in knooppunt te meitsjen, moatte jo ynteraksje mei de MSP.

Peer knopen - ferifiearje transaksjes, bewarje de blockchain, útfiere tûke kontrakten en ynteraksje mei applikaasjes. Peers hawwe in identiteit (digitaal sertifikaat), dat wurdt útjûn troch de MSP. Oars as it Bitcoin of Etherium netwurk, dêr't alle knopen hawwe gelikense rjochten, yn Hyperledger Fabric knopen spylje ferskillende rollen:

  • Peer miskien ûnderskriuwe peer (EP) en smart kontrakten útfiere.
  • Ynsette peer (CP) - bewarje allinich gegevens yn 'e blockchain en aktualisearje de "Wrâldsteat".
  • Anker Peer (AP) - as ferskate organisaasjes dielnimme oan 'e blockchain, dan wurde ankerpeers brûkt foar kommunikaasje tusken har. Elke organisaasje moat ien of mear anker-peers hawwe. Mei AP kin elke peer yn in organisaasje ynformaasje krije oer alle peers yn oare organisaasjes. Wurdt brûkt om ynformaasje te syngronisearjen tusken AP's gossip protokol.
  • Leader Peer - as in organisaasje ferskate leeftydsgenoaten hat, dan sil allinich de lieder fan 'e peer blokken krije fan 'e Bestellingstsjinst en jouwe se oan 'e rest fan' e peers. De lieder kin statysk of dynamysk selektearre wurde troch peers yn 'e organisaasje. It roddelprotokol wurdt ek brûkt om ynformaasje oer lieders te syngronisearjen.

Aktiva - entiteiten dy't wearde hawwe en wurde opslein op 'e blockchain. Mear spesifyk binne dit kaai-weardegegevens yn JSON-formaat. It is dizze gegevens dy't opnommen binne yn 'e Blockchain. Se hawwe in skiednis, dy't opslein is yn 'e blockchain, en in aktuele steat, dy't opslein is yn' e "Wrâldsteat" databank. Gegevensstruktueren wurde willekeurich ynfolle ôfhinklik fan saaklike taken. D'r binne gjin fereaske fjilden, de ienige oanbefelling is dat fermogen in eigner moatte hawwe en weardefol wêze moatte.

Ledger - bestiet út de Blockchain en de Word-statedatabase, dy't de hjoeddeistige steat fan aktiva opslacht. Wrâldsteat brûkt LevelDB of CouchDB.

Tûk kontrakt - mei help fan tûke kontrakten wurdt de saaklike logika fan it systeem ymplementearre. Yn Hyperledger Fabric wurde tûke kontrakten chaincode neamd. Mei help fan chaincode, aktiva en transaksjes oer harren wurde oantsjutte. Yn technyske termen binne tûke kontrakten softwaremodules ymplementearre yn 'e JS- as Go-programmearringstalen.

Endossement belied - foar elke chaincode kinne jo in belied ynstelle oer hoefolle befêstigings foar in transaksje moatte wurde ferwachte en fan wa. As it belied net ynsteld is, dan is de standert: "de transaksje moat wurde befêstige troch elk lid fan elke organisaasje yn it kanaal." Foarbylden fan belied:

  • De transaksje moat wurde goedkard troch elke behearder fan 'e organisaasje;
  • Moat wurde befêstige troch elk lid of kliïnt fan 'e organisaasje;
  • Moat wurde befêstige troch elke peer-organisaasje.

Bestelle tsjinst - pakt transaksjes yn blokken en stjoert se nei peers yn it kanaal. Garandearret levering fan berjochten oan alle peers op it netwurk. Wurdt brûkt foar yndustriële systemen Kafka berjochtmakelaar, foar ûntwikkeling en testen solo.

CallFlow

Blockchain: hokker PoC moatte wy bouwe?

  • De applikaasje kommunisearret mei Hyperledger Fabric mei Go, Node.js of Java SDK;
  • De kliïnt makket in tx-transaksje en stjoert it nei ûnderskriuwende peers;
  • De Peer ferifieart de hantekening fan 'e kliïnt, foltôget de transaksje en stjoert de ûndertekeningsûndertekening werom nei de kliïnt. Chaincode wurdt allinich útfierd op 'e ûnderskriuwende peer, en it resultaat fan syn útfiering wurdt stjoerd nei alle peers. Dit algoritme fan wurk wurdt PBFT (Practical Byzantine Fault Tolerant) konsensus neamd. Ferskil fan klassike BFT it feit dat it berjocht wurdt ferstjoerd en befêstiging wurdt ferwachte net fan alle dielnimmers, mar allinnich fan in bepaalde set;
  • Nei't de klant it oantal antwurden hat krigen dat oerienkomt mei it goedkarringbelied, stjoert hy de transaksje nei de Bestellingstsjinst;
  • De Ordering-tsjinst genereart in blok en stjoert it nei alle ynsette peers. Bestellingstsjinst soarget foar sekwinsjele opname fan blokken, wat de saneamde ledgerfork elimineert (sjoch seksje "Forks");
  • Peers krije in blok, kontrolearje it beoardielingsbelied nochris, skriuw it blok nei de blockchain en feroarje de steat yn 'e "Wrâldsteat" DB.

Dy. Dit resultearret yn in ferdieling fan rollen tusken de knopen. Dit soarget derfoar dat de blockchain skaalber en feilich is:

  • Smart kontrakten (chaincode) útfiere ûnderskriuwe peers. Dit soarget foar de fertroulikens fan smart kontrakten, omdat it wurdt net opslein troch alle dielnimmers, mar allinnich troch ûnderskriuwen fan leeftydsgenoaten.
  • Bestelle moat fluch wurkje. Dit wurdt garandearre troch it feit dat Ordering allinnich foarmet in blok en stjoert it nei in fêste set fan lieder peers.
  • Ynsette peers bewarje allinich de blockchain - d'r kinne in protte fan wêze en se hawwe net in protte macht en direkte operaasje nedich.

Mear details oer de arsjitektoanyske oplossingen fan Hyperledger Fabric en wêrom it op dizze manier wurket en net oars kinne jo hjir fine: Arsjitektuer oarsprong of hjir: Hyperledger Fabric: In ferspraat bestjoeringssysteem foar tastiene blockchains.

Dat, Hyperledger Fabric is in wirklik universeel systeem wêrmei jo kinne:

  • Implementearje willekeurige saaklike logika mei it tûke kontraktmeganisme;
  • Opnimme en ûntfange gegevens fan 'e blockchain-database yn JSON-formaat;
  • Grant en ferifiearje API tagong mei Certificate Authority.

No't wy in bytsje begripe oer de spesifiken fan Hyperledger Fabric, litte wy einlings wat nuttich dwaan!

It ynsetten fan blockchain

Probleemintwurding

De taak is om it Citcoin-netwurk út te fieren mei de folgjende funksjes: in akkount meitsje, in balâns krije, jo akkount oanfolje, munten oermeitsje fan it iene akkount nei it oare. Litte wy in objektmodel tekenje, dat wy fierder sille ymplementearje yn in tûk kontrakt. Dat, wy sille akkounts hawwe dy't wurde identifisearre troch nammen en befetsje in saldo, en in list mei akkounts. Akkounts en in list mei akkounts binne, yn termen fan Hyperledger Fabric-aktiva. Dêrtroch hawwe se in skiednis en in hjoeddeistige steat. Ik sil besykje dit dúdlik te tekenjen:

Blockchain: hokker PoC moatte wy bouwe?

De boppeste sifers binne de aktuele steat, dy't opslein is yn 'e databank "Wrâldsteat". Hjirûnder steane sifers dy't de skiednis sjen litte dy't opslein is yn 'e blockchain. De hjoeddeistige steat fan aktiva wurdt feroare troch transaksjes. De Asset feroaret allinich as gehiel, dus as gefolch fan 'e transaksje wurdt in nij objekt makke, en de hjoeddeistige wearde fan' e asset giet yn 'e skiednis.

IBM Cloud

Wy meitsje in akkount yn IBM wolk. Om it blockchain-platfoarm te brûken, moat it opwurdearre wurde nei Pay-As-You-Go. Dit proses is miskien net fluch, om't ... IBM freget ekstra ynformaasje en ferifiearret it mei de hân. Op in positive noat kin ik sizze dat IBM goede trainingsmateriaal hat wêrmei jo Hyperledger Fabric yn har wolk kinne ynsette. Ik mocht graach de folgjende searje artikels en foarbylden:

De folgjende binne skermôfbyldings fan it IBM Blockchain-platfoarm. Dit is gjin ynstruksje oer hoe't jo in blockchain meitsje, mar gewoan in demonstraasje fan 'e omfang fan' e taak. Dat, foar ús doelen, meitsje wy ien organisaasje:

Blockchain: hokker PoC moatte wy bouwe?

Wy meitsje knooppunten yn: Orderer CA, Org1 CA, Orderer Peer:

Blockchain: hokker PoC moatte wy bouwe?

Wy meitsje brûkers:

Blockchain: hokker PoC moatte wy bouwe?

Meitsje in kanaal en neam it citcoin:

Blockchain: hokker PoC moatte wy bouwe?

Yn essinsje is Channel in blockchain, dus it begjint mei blok nul (Genesis-blok):

Blockchain: hokker PoC moatte wy bouwe?

It skriuwen fan in tûk kontrakt

/*
 * Citcoin smart-contract v1.5 for Hyperledger Fabric
 * (c) Alexey Sushkov, 2019
 */
 
'use strict';
 
const { Contract } = require('fabric-contract-api');
const maxAccounts = 5;
 
class CitcoinEvents extends Contract {
 
    async instantiate(ctx) {
        console.info('instantiate');
        let emptyList = [];
        await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(emptyList)));
    }
    // Get all accounts
    async GetAccounts(ctx) {
        // Get account list:
        let accounts = '{}'
        let accountsData = await ctx.stub.getState('accounts');
        if (accountsData) {
            accounts = JSON.parse(accountsData.toString());
        } else {
            throw new Error('accounts not found');
        }
        return accountsData.toString()
    }
     // add a account object to the blockchain state identifited by their name
    async AddAccount(ctx, name, balance) {
        // this is account data:
        let account = {
            name: name,
            balance: Number(balance),       
            type: 'account',
        };
        // create account:
        await ctx.stub.putState(name, Buffer.from(JSON.stringify(account)));
 
        // Add account to list:
        let accountsData = await ctx.stub.getState('accounts');
        if (accountsData) {
            let accounts = JSON.parse(accountsData.toString());
            if (accounts.length < maxAccounts)
            {
                accounts.push(name);
                await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(accounts)));
            } else {
                throw new Error('Max accounts number reached');
            }
        } else {
            throw new Error('accounts not found');
        }
        // return  object
        return JSON.stringify(account);
    }
    // Sends money from Account to Account
    async SendFrom(ctx, fromAccount, toAccount, value) {
        // get Account from
        let fromData = await ctx.stub.getState(fromAccount);
        let from;
        if (fromData) {
            from = JSON.parse(fromData.toString());
            if (from.type !== 'account') {
                throw new Error('wrong from type');
            }   
        } else {
            throw new Error('Accout from not found');
        }
        // get Account to
        let toData = await ctx.stub.getState(toAccount);
        let to;
        if (toData) {
            to = JSON.parse(toData.toString());
            if (to.type !== 'account') {
                throw new Error('wrong to type');
            }  
        } else {
            throw new Error('Accout to not found');
        }
 
        // update the balances
        if ((from.balance - Number(value)) >= 0 ) {
            from.balance -= Number(value);
            to.balance += Number(value);
        } else {
            throw new Error('From Account: not enought balance');          
        }
 
        await ctx.stub.putState(from.name, Buffer.from(JSON.stringify(from)));
        await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
                 
        // define and set Event
        let Event = {
            type: "SendFrom",
            from: from.name,
            to: to.name,
            balanceFrom: from.balance,
            balanceTo: to.balance,
            value: value
        };
        await ctx.stub.setEvent('SendFrom', Buffer.from(JSON.stringify(Event)));
 
        // return to object
        return JSON.stringify(from);
    }
 
    // get the state from key
    async GetState(ctx, key) {
        let data = await ctx.stub.getState(key);
        let jsonData = JSON.parse(data.toString());
        return JSON.stringify(jsonData);
    }
    // GetBalance   
    async GetBalance(ctx, accountName) {
        let data = await ctx.stub.getState(accountName);
        let jsonData = JSON.parse(data.toString());
        return JSON.stringify(jsonData);
    }
     
    // Refill own balance
    async RefillBalance(ctx, toAccount, value) {
        // get Account to
        let toData = await ctx.stub.getState(toAccount);
        let to;
        if (toData) {
            to = JSON.parse(toData.toString());
            if (to.type !== 'account') {
                throw new Error('wrong to type');
            }  
        } else {
            throw new Error('Accout to not found');
        }
 
        // update the balance
        to.balance += Number(value);
        await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
                 
        // define and set Event
        let Event = {
            type: "RefillBalance",
            to: to.name,
            balanceTo: to.balance,
            value: value
        };
        await ctx.stub.setEvent('RefillBalance', Buffer.from(JSON.stringify(Event)));
 
        // return to object
        return JSON.stringify(from);
    }
}
module.exports = CitcoinEvents;

Yntuïtyf soe alles hjir dúdlik wêze moatte:

  • D'r binne ferskate funksjes (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) dat it demoprogramma sil neame mei de Hyperledger Fabric API.
  • De funksjes SendFrom en RefillBalance generearje eveneminten dy't it demoprogramma sil ûntfange.
  • De instantiatefunksje wurdt ien kear neamd as in tûk kontrakt wurdt instantiearre. Yn feite wurdt it net ien kear neamd, mar elke kear feroaret de smart kontraktferzje. Dêrom is it inisjalisearjen fan in list mei in lege array in min idee, om't No, as wy de ferzje fan it smart kontrakt feroarje, sille wy de hjoeddeistige list ferlieze. Mar it is goed, ik lear gewoan).
  • Akkounts en in list mei akkounts binne JSON-gegevensstruktueren. JS wurdt brûkt foar gegevensmanipulaasje.
  • Jo kinne de hjoeddeistige wearde fan in asset krije mei de getState-funksjeoprop, en it bywurkje mei putState.
  • By it oanmeitsjen fan in akkount wurdt de AddAccount-funksje neamd, wêryn in fergeliking makke wurdt foar it maksimale oantal akkounts yn 'e blockchain (maxAccounts = 5). En hjir is d'r in jamb (ha jo opfallen?), Wat liedt ta in einleaze taname fan it oantal akkounts. Sokke flaters moatte foarkommen wurde)

Folgjende laden wy it tûke kontrakt yn it kanaal en ynstantiearje it:

Blockchain: hokker PoC moatte wy bouwe?

Litte wy nei de transaksje sjen foar it ynstallearjen fan Smart Contract:

Blockchain: hokker PoC moatte wy bouwe?

Litte wy nei de details oer ús kanaal sjen:

Blockchain: hokker PoC moatte wy bouwe?

As gefolch krije wy it folgjende diagram fan in blockchain-netwurk yn 'e IBM-wolk. It diagram toant ek in demo-programma dat rint yn 'e Amazon-wolk op in firtuele server (mear deroer yn' e folgjende paragraaf):

Blockchain: hokker PoC moatte wy bouwe?

It meitsjen fan in GUI foar Hyperledger Fabric API-oproppen

Hyperledger Fabric hat in API dy't kin wurde brûkt om:

  • Meitsje kanaal;
  • Ferbinings peer nei kanaal;
  • Ynstallaasje en ynstânsje fan tûke kontrakten yn it kanaal;
  • Calling transaksjes;
  • Fersykje ynformaasje oer de blockchain.

Applikaasjeûntwikkeling

Yn ús demoprogramma sille wy de API allinich brûke om transaksjes op te roppen en ynformaasje te freegjen, om't Wy hawwe de oerbleaune stappen al foltôge mei it IBM blockchain-platfoarm. Wy skriuwe in GUI mei help fan in standert technology stack: Express.js + Vue.js + Node.js. Jo kinne in apart artikel skriuwe oer hoe't jo begjinne mei it meitsjen fan moderne webapplikaasjes. Hjir sil ik in keppeling litte nei de searje lêzingen dy't ik it meast leuk fûn: Full Stack Web App mei Vue.js & Express.js. It resultaat is in client-server-applikaasje mei in fertroude grafyske interface yn Google's Material Design-styl. De REST API tusken client en server bestiet út ferskate oproppen:

  • HyperledgerDemo/v1/init - inisjalisearje de blockchain;
  • HyperledgerDemo/v1/accounts/list - krije in list mei alle akkounts;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 — meitsje Bob-akkount;
  • HyperledgerDemo/v1/info?account=Bob — krije ynformaasje oer Bob-akkount;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 - oerdrage twa munten fan Bob nei Alice;
  • HyperledgerDemo/v1/disconnect - slút de ferbining mei de blockchain.

Beskriuwing fan 'e API mei foarbylden opnommen yn Postman webside - in bekend programma foar testen fan HTTP API.

Demo-applikaasje yn Amazon-wolk

Ik haw de applikaasje upload nei Amazon om't ... IBM hat noch altyd net by steat west om te upgrade myn akkount en lit my meitsje firtuele tsjinners. Hoe kinne jo in kers tafoegje oan it domein: www.citcoin.info. Ik hâld de tsjinner in skoftke oan, en dan útsette, want... sinten foar hier drippe, en citcoin-munten binne noch net fermeld op 'e beurs) Ik bin ynklusyf screenshots fan de demo yn it artikel sadat de logika fan it wurk is dúdlik. De demo-applikaasje kin:

  • Inisjalisearje de blockchain;
  • Meitsje in akkount (mar no kinne jo gjin nij akkount meitsje, om't it maksimale oantal akkounts oantsjutte yn 'e tûke kontrakt is berikt yn' e blockchain);
  • Untfang in list mei akkounts;
  • Oermeitsje citcoin munten tusken Alice, Bob en Alex;
  • Untfang eveneminten (mar no is d'r gjin manier om eveneminten te sjen, dus foar ienfâld seit de ynterface dat eveneminten net wurde stipe);
  • Log aksjes.

Earst inisjalisearje wy de blockchain:

Blockchain: hokker PoC moatte wy bouwe?

Dêrnei meitsje wy ús akkount, fergrieme gjin tiid mei it saldo:

Blockchain: hokker PoC moatte wy bouwe?

Wy krije in list mei alle beskikbere akkounts:

Blockchain: hokker PoC moatte wy bouwe?

Wy selektearje de stjoerder en ûntfanger, en krije harren saldo. As de stjoerder en de ûntfanger itselde binne, dan sil syn akkount wurde oanfolle:

Blockchain: hokker PoC moatte wy bouwe?

Yn it log kontrolearje wy de útfiering fan transaksjes:

Blockchain: hokker PoC moatte wy bouwe?

Eins is dat alles mei it demoprogramma. Hjirûnder kinne jo ús transaksje sjen yn 'e blockchain:

Blockchain: hokker PoC moatte wy bouwe?

En de algemiene list fan transaksjes:

Blockchain: hokker PoC moatte wy bouwe?

Hjirmei hawwe wy de ymplemintaasje fan 'e PoC mei súkses foltôge om it Citcoin-netwurk te meitsjen. Wat oars moat dien wurde foar Citcoin om in folweardich netwurk te wurden foar it oerdragen fan munten? Hiel lyts:

  • Implementearje yn it oanmeitsjen fan akkounts de generaasje fan in privee / iepenbiere kaai. De privee kaai moat wurde opslein mei de akkount brûker, de iepenbiere kaai moat wurde opslein yn 'e blockchain.
  • Meitsje in muntoerdracht wêryn in iepenbiere kaai, ynstee fan in namme, wurdt brûkt om de brûker te identifisearjen.
  • Fersiferje transaksjes fan 'e brûker nei de tsjinner mei syn privee kaai.

konklúzje

Wy hawwe it Citcoin-netwurk ymplementearre mei de folgjende funksjes: in akkount tafoegje, in balâns krije, jo akkount oanfolje, munten oermeitsje fan it iene akkount nei it oare. Dat, wat hat it ús koste om in PoC te bouwen?

  • Jo moatte studearje blockchain yn it algemien en Hyperledger Fabric yn it bysûnder;
  • Learje om IBM- of Amazon-wolken te brûken;
  • Learje de JS-programmearringstaal en wat webkader;
  • As guon gegevens moatte wurde opslein net yn 'e blokcheyn, mar yn in aparte databank, dan leare te yntegrearjen, bygelyks, mei PostgreSQL;
  • En last but not least - jo kinne net libje yn 'e moderne wrâld sûnder kennis fan Linux!)

Fansels is it gjin raketwittenskip, mar jo moatte hurd wurkje!

Boarnen op GitHub

Boarnen sette op GitHub. Koarte beskriuwing fan it repository:
Katalogus «tsjinner» - Node.js-tsjinner
Katalogus «kliïnt» - Node.js client
Katalogus «blockchain"(parameterwearden en toetsen, fansels, wurkje net en wurde allinich as foarbyld jûn):

  • kontrakt - smart kontrakt boarne koade
  • wallet - brûkerskaaien foar it brûken fan de Hyperledger Fabric API.
  • *.cds - gearstalde ferzjes fan smart kontrakten
  • *.json triemmen - foarbylden fan konfiguraasje triemmen foar it brûken fan de Hyperledger Fabric API

It is mar it begjin!

Boarne: www.habr.com

Add a comment