Blockchain: what does it take to build a PoC?

Silmad kardavad, aga kĂ€ed sĂŒgelevad!

Eelmistes artiklites kĂ€sitlesime tehnoloogiaid, millel plokiahelad pĂ”hinevad (Mis meil maksab plokiahela ĂŒlesehitamine?) ja juhtumeid, mida nende abil ellu viia (Mida on vaja juhtumi loomiseks?). On aeg hakata kĂ€t tööle panema! Pilootide ja PoC (tĂ”estus kontseptsioonist) elluviimiseks eelistan kasutada pilveteenuseid, kuna neile pÀÀseb juurde igast maailma nurgast ning tihti ei pea kulutama aega tĂŒĂŒtule keskkonna seadistamisele, kuna on olemas eelnevalt seadistatud konfiguratsioonid. Alustame lihtsa asjaga, nĂ€iteks vĂ”rguga, et saata mĂŒnte osalejate vahel ning nimetame seda tagasihoidlikult Citooniks. Selleks kasutame IBM-i pilve ja universaalset plokiahelat Hyperledger Fabric. Esiteks, miks nimetatakse Hyperledger Fabricit universaalseks plokiahelaks?

Blockchain: what does it take to build a PoC?

Hyperledger Fabric — universaalne plokiahel

Üldiselt öeldes on universaalne teabehalduse sĂŒsteem:

  • Serverite kogum ja tarkvaratuum, mis teostab Ă€riloogikat;
  • Liidesed sĂŒsteemiga suhtlemiseks;
  • Seadmete/inimeste registreerimise, autentimise ja autoriseerimise vahendid;
  • Andmebaas, mis salvestab operatiivseid ja arhiivandmeid:

Blockchain: what does it take to build a PoC?

Ametlikku versiooni sellest, mis on Hyperledger Fabric, saab lugeda veebilehel, ja lĂŒhidalt öeldes on Hyperledger Fabric avatud lĂ€htekoodiga platvorm, mis vĂ”imaldab luua erablokke ja teostada mis tahes nutikaid lepinguid, mis on kirjutatud programmeerimiskeeltes JS ja Go. Vaadakem lĂ€hemalt Hyperledger Fabric'i arhitektuuri ja veendugem, et see on universaalne sĂŒsteem, millel on vaid spetsiifilised andmete salvestamise ja kirjutamise omadused. Spetsiifilisus seisneb selles, et andmed, nagu kĂ”igis blokeeringutes, salvestatakse blokkidesse, mis lisatakse plokiahelasse ainult siis, kui osalejad on jĂ”udnud konsensusele, ja pĂ€rast salvestamist ei saa andmeid mĂ€rkamatult muuta ega kustutada.

Hyperledger Fabric'i arhitektuur

Diagrammil on nÀidatud Hyperledger Fabric'i arhitektuur:

Blockchain: what does it take to build a PoC?

Organisatsioonid — organisatsioonid sisaldavad peer'e, seega eksisteerib plokiahel organisatsioonide toetuse tĂ”ttu. Erinevad organisatsioonid vĂ”ivad kuuluda ĂŒhte kanalisse.

Kanal — loogiline struktuur, mis ĂŒhendab peer'e gruppidesse, seega mÀÀratleb see plokiahela. Hyperledger Fabric suudab samal ajal töödelda mitut plokiahelat erineva Ă€ri loogikaga.

Liikmesuse teenuste pakkuja (MSP) — on CA (Certificate Authority) identiteedi vĂ€ljastamiseks ja rollide mÀÀramiseks. Nodede loomiseks tuleb suhelda MSP-ga.

Peer nodid — kontrollivad tehinguid, salvestavad plokiahelat, tĂ€idavad nutilepinguid ja suhtlevad rakendustega. Peeridel on identiteet (digitaalne sertifikaat), mille vĂ€ljastab MSP. Erinevalt Bitcoin vĂ”i Etheriumi vĂ”rgust, kus kĂ”ik nodid on vĂ”rdsed, mĂ€ngivad Hyperledger Fabricis nodid erinevaid rolle:

  • Peer vĂ”ib olla endorsing peer (EP) ja tĂ€ita nutilepinguid.
  • Committing peer (CP) – salvestavad ainult andmeid plokiahelas ja ajakohastavad 'World state'.
  • Anchor Peer (AP) – kui plokiahelas osaleb mitu organisatsiooni, siis kasutatakse ankur peer-e omavaheliseks ĂŒhendamiseks. Iga organisatsioon peaks omama ĂŒhte vĂ”i mitut ankur peer-i. AP kaudu saab iga peer organisatsioonis teavet kĂ”ikide peer-ide kohta teistes organisatsioonides. AP-de vahelise teabe sĂŒnkroniseerimiseks kasutatakse gossip protokolli.
  • Leader Peer — kui organisatsioonil on mitu peer'i, siis ainult juhtpeer saab blokke Ordering service'ilt ja jagab neid teiste peer'ide hulka. Juht vĂ”ib olla mÀÀratud staatiliselt vĂ”i valida dĂŒnaamiliselt peer'ide seas organisatsioonis. Infovahetuseks juhtide ĂŒle kasutatakse samuti gossip protokolli.

Varad — vÀÀrtuslikud ĂŒksused, mida hoitakse plokiahelas. TĂ€psemalt on need key-value andmed JSON formaadis. Just need andmed salvestatakse plokiahelasse „Blockchain”. Neil on ajalugu, mis salvestatakse plokiahelas, ja hetke seis, mis salvestatakse andmebaasis „World state”. Andmestruktuurid tĂ€idetakse vastavalt Ă€riĂŒlesannetele. Kohustuslikke vĂ€lju ei ole, ainus soovitus on, et varadel peaks olema omanik ja need peaksid omama vÀÀrtust.

Raamatupidamine — koosneb plokiahest „Blockchain” ja andmebaasist „World state”, kus hoitakse varade hetke seise. World state kasutab LevelDB-d vĂ”i CouchDB-d.

Nutileping — Ă€riga smartlepingute abil rakendatakse sĂŒsteemi Ă€ri loogikat. Hyperledger Fabricis nimetatakse smartlepinguid chaincode'iks. Chaincode abil mÀÀratakse varad ja nendega seotud tehingud. Tehniliselt öeldes on smartlepingud programmimoodulid, mis on kirjutatud programmeerimiskeeltes JS vĂ”i Go.

Kinnituspolitika — iga chaincode jaoks saab mÀÀrata poliitikad, kui palju ja kellelt tuleb tehingu kinnitusi oodata. Kui poliitikat ei ole mÀÀratud, kasutatakse vaikimisi: „tehingu peab kinnitama ĂŒkskĂ”ik milline liige (member) ĂŒkskĂ”ik millisest organisatsioonist kanalil (channel).” NĂ€iteid poliitikatest:

  • Tehingu peab kinnitama igasugune organisatsiooni administraator;
  • Peab kinnitama iga liige (member) vĂ”i organisatsiooni klient;
  • Peab kinnitama iga organisatsiooni peer.

Tellimisteenus — pakib tehingud plokkidesse ja saadab need peer'idele kanalisse. Tagab sĂ”numite saatmise kĂ”igile vĂ”rgus olevatele peer'idele. Tööstuslikes sĂŒsteemides kasutatakse Kafka sĂ”numivahendajat, arenduseks ja testimiseks Solo.

CallFlow

Blockchain: what does it take to build a PoC?

  • Rakendus suhtleb Hyperledger Fabriciga, kasutades Go, Node.js vĂ”i Java SDK;
  • Kliendil on tehing tx ja ta saadab selle kinnitavatele peer'idele;
  • Peer kontrollib kliendi allkirja, viib tehingu lĂ€bi ja saadab endorsement signature kliendile tagasi. Chaincode töötab ainult endorsing peer'il ja selle tĂ€itmise tulemus edastatakse kĂ”ikidele peer'idele. See töömehanism on tuntud kui PBFT (Practical Byzantine Fault Tolerant) konsensus. See erineb klassikalisest BFT selle poolest, et sĂ”num saadetakse ja oodatakse kinnitust mitte kĂ”igilt osalistelt, vaid ainult kindlast hulgast;
  • PĂ€rast seda, kui klient on saanud vastuseid, mis vastavad endorsement policy'le, saadab ta tehingu Ordering service'ile;
  • Ordering service koostab ploki ja saadab selle kĂ”ikidele committing peer'idele. Ordering service tagab plokkide jĂ€rjestikuse salvestamise, mis vĂ€listab nn ledger fork (vt jaotist „Forkid”);
  • Peer'id saavad ploki, kontrollivad uuesti endorsement policy't, salvestavad ploki plokiahelasse ja muudavad seisundit „World state” DB-s.

St. see tagab rollide jagamise sÔlmepunktide vahel. See vÔimaldab skalaarida ja suurendada plokiahela turvalisust:

  • Nutilepingud (chaincode) tĂ€idavad endorsing peer'id. See tagab nutilepingute konfidentsiaalsuse, kuna need ei ole kĂ”ikide osalejate juures, vaid ainult endorsing peer'idel.
  • Tellimus peab toimuma kiiresti. Seda tagab asjaolu, et tellimus loob ainult plokki ja saadab selle kindlale rikkapeeride kogumile.
  • Kohustavad peers lihtsalt hoiavad plokiahelat — neid vĂ”ib olla palju ja nad ei nĂ”ua suurt vĂ”imsust ega kiiret toimimist.

Üksikasjalikumalt arhitektuuri lahendustest Hyperledger Fabric ja miks see töötab just nii, saab vaadata siit: Arhitektuuri algused vĂ”i sealt: Hyperledger Fabric: jaotatud opsĂŒsteem privileege saanud plokiahelatele.

Nii et Hyperledger Fabric on tĂ”eliselt universaalne sĂŒsteem, mille abil saab:

  • Rakendada suvalist Ă€riloogikat, kasutades nutikate lepingute mehhanismi;
  • Salvestada ja saada andmeid plokiahela andmebaasist JSON-formaadis;
  • Osutada ja kontrollida ligipÀÀsu API-le, kasutades sertifitseerimisasutust.

NĂŒĂŒd, kui oleme veidi tutvunud Hyperledger Fabric eripĂ€radega, teeme lĂ”puks midagi kasulikku!

Seame ĂŒles plokiahela

Ülesande seadmine

Ülesanne on luua Citcoin vĂ”rk jĂ€rgmiste funktsioonidega: konto loomine, saldo saamine, konto tĂ€iendamine ja mĂŒntide ĂŒlekandmine ĂŒhest kontost teise. Joonistame objekti mudeli, mille hiljem rakendame nutilepingus. Seega on meil kontod, mida tuvastatakse nimede (name) ja mis sisaldavad saldo (balance) ning konto loend. Kontod ja konto loend on Hyperledger Fabric'i mĂ”istes varad (assets). Vastavalt on neil ajalugu ja hetkeolek. Proovin seda visuaalselt illustreerida:

Blockchain: what does it take to build a PoC?

Ülemised kujundid on hetkeolek, mis on salvestatud andmebaasi „World state“. Allpool on kujundid, mis nĂ€itavad ajalugu, mis salvestatakse plokiahelas. Varade hetkeolekut muudetakse tehingute kaudu. Vara muutub ainult tervikuna, seega tehingu tĂ€itmise tulemusena luuakse uus objekt ja vara hetkevÀÀrtus lĂ€heb ajalukku.

IBM Cloud

Loome konto IBM Cloude. EttevĂ”tte blockchain platvormi kasutamiseks tuleb seda uuendada Pay-As-You-Go mudelile. See protsess vĂ”ib olla ajamahukas, kuna IBM kĂŒsib lisainfot ja kontrollib seda kĂ€sitsi. Positiivse poole pealt vĂ”in öelda, et IBM-il on head Ă”pikurid, mis aitavad Hyperledger Fabricit nende pilves kĂ€ivitada. Mulle meeldis jĂ€rgmine artikkel ja nĂ€idisprotsesside tsĂŒkkel:

JĂ€rgnevalt tuuakse vĂ€lja IBM Blockchain platvormi ekraanipildid. See pole juhend blockchain'i loomise kohta, vaid lihtsalt ĂŒlesande ulatuse nĂ€itamine. Nii et meie eesmĂ€rkide jaoks loome ĂŒhe organisatsiooni:

Blockchain: what does it take to build a PoC?

Selle alla loome sÔlmed: Orderer CA, Org1 CA, Orderer Peer:

Blockchain: what does it take to build a PoC?

Loome kasutajad:

Blockchain: what does it take to build a PoC?

Loo kanal ja nimeta see citcoiniks:

Blockchain: what does it take to build a PoC?

Sisuliselt on kanal blockchain, seega algab see nullplokist (Genesis block):

Blockchain: what does it take to build a PoC?

Kirjuta nutileping

/*
 * 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;

Intuitiivselt peaks siin kÔik selge olema:

  • On mitu funktsiooni (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance), mida demoprogramm kutsub Hyperledger Fabric API kaudu.
  • Funktsioonid SendFrom ja RefillBalance genereerivad sĂŒndmusi (Event), mida demoprogramm saab.
  • instantiate funktsioon kĂ€ivitatakse ĂŒks kord nutilepingu instantsimise ajal. Tegelikult kĂ€ivitatakse see mitte ĂŒhe, vaid iga kord, kui nutilepingut muudetakse. Seega on loetelu algvÀÀrtustamine tĂŒhi massiiv — halb mĂ”te, sest nĂŒĂŒd, kui nutilepingut uuendatakse, kaotame me praeguse loendi. Aga pole hullu, ma alles Ă”pin).
  • Account-id ja account-ide (accounts) loend on JSON andmestruktuurid. Andmete manipuleerimiseks kasutatakse JS-i.
  • VĂ”ite praeguse asset-i vÀÀrtuse saada, kasutades funktsiooni getState, ja uuendada seda funktsiooniga putState.
  • Account-i loomisel kĂ€ivitatakse funktsioon AddAccount, kus vĂ”rreldakse blockchainis maksimaalset account-ide arvu (maxAccounts = 5). Ja siin on aps (kas mĂ€rkate?), mis viib account-ide arvu lĂ”pmatu kasvu. Selliseid vigu tuleks vĂ€ltida).

SeejÀrel laadime nutilepingut Channelisse ja instantsime selle:

Blockchain: what does it take to build a PoC?

Vaadake Smart Contracti seadistamise tehingut:

Blockchain: what does it take to build a PoC?

Vaadake meie Channeli ĂŒksikasju:

Blockchain: what does it take to build a PoC?

Saame jĂ€rgmise plaani IBM-i pilve blockchaini vĂ”rgust. Skeemil on samuti demo programm, mis töötab Amazonis virtuaalses serveris (ĂŒksikasjad on jĂ€rgmises peatĂŒkis):

Blockchain: what does it take to build a PoC?

GUI loomine Hyperledger Fabric API kutseteks

Hyperledger Fabric'il on API, mida saab kasutada:

  • kanali loomiseks;
  • peer'i ĂŒhendamiseks kanaliga;
  • tarkade lepingute seadistamiseks ja instantsimiseks kanalil;
  • tehingute kutsumiseks;
  • informatsiooni pĂ€rimiseks blockchainis.

Rakenduse arendamine

Kasutame meie demo programmis API-d ainult tehingute kutsmiseks ja informatsiooni pÀrimiseks, kuna kÔik muud sammud on juba tehtud, kasutades IBM-i blockchain platvormi. Loome GUI, kasutades standardtöötleja tehnoloogiaid: Express.js + Vue.js + Node.js. Kuidas alustada tÀnapÀevaste veebirakenduste loomist vÔib kirjutada eraldi artiklis. Siin on link loengute seeriale, mis mulle kÔige rohkem meeldis: Full Stack Web App using Vue.js & Express.js. Tulemuseks on kliendi-serveri rakendus tuttava graafilise liidesega, mis jÀrgib Google'i Material Design'i stiili. REST API kliendi ja serveri vahel koosneb mitmest kutsetest:

  • HyperledgerDemo/v1/init — blockchaini initsialiseerimine;
  • HyperledgerDemo/v1/accounts/list — saada kĂ”igi kontode nimekiri;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 — loo konto Bob;
  • HyperledgerDemo/v1/info?account=Bob — saa teavet Bob konto kohta;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — saada kaks mĂŒnter Bob-ilt Alice'ile;
  • HyperledgerDemo/v1/disconnect — katkesta ĂŒhendus plokiahelaga.

API kirjeldus koos nĂ€idetega on saadaval veebilehel "Postman" — laialdaselt tuntud programm HTTP API testimiseks.

Demorakendus Amazonis

Olen rakenduse ĂŒles laadinud Amazonile, kuna IBM pole siiani suudnud minu kontot uuendada ja lubada virtuaalsete serverite loomist. Nagu kirsiks tordil, lisasin domeeni: www.citcoin.info. Hoian serverit natuke sisse lĂŒlitatuna, siis lĂŒlitan vĂ€lja, kuna renti kajastuvad sendid ja citcoin mĂŒndid börsil pole veel noteeritud) Artiklis lisasin ekraanipildid demost, et oleks arusaadav tööloogika. Demorakendus saab:

  • Algatada plokiahela;
  • Luua konto (kuid praegu uut kontot luua ei saa, kuna plokiahelas on saavutatud maksimaalne arvu kontosid, mis on mÀÀratud nutilepingus);
  • Saada konto nimekiri;
  • Üle kanda citcoin mĂŒnte Alice, Bobi ja Alexi vahel;
  • Saada sĂŒndmusi (aga hetkel ei saa sĂŒndmusi mingil moel nĂ€idata, seega on liideses lihtsuse huvides kirjas, et sĂŒndmusi ei toetata);
  • Logige tegevused.

Esialgu initsialiseerime plokiahela:

Blockchain: what does it take to build a PoC?

SeejÀrel loome oma konto, mitte ei hÔlma pÔhjaga:

Blockchain: what does it take to build a PoC?

Saame kÔikide saadaval olevate kontode loendi:

Blockchain: what does it take to build a PoC?

Valime saatja ja saaja, saame nende bilansid. Kui saatja ja saaja on sama, siis toimub tema konto tÀiendamine:

Blockchain: what does it take to build a PoC?

Logis jÀlgime tehingute tÀitmist:

Blockchain: what does it take to build a PoC?

Demoprogrammiga on see kÔik. JÀrgmine samm on meie tehingu vaatamine plokiahelas:

Blockchain: what does it take to build a PoC?

Ja tehingute ĂŒldine nimekiri:

Blockchain: what does it take to build a PoC?

Sellega oleme edukalt lĂ”petanud Citcoin vĂ”rgu PoC rakendamise. Mida veel on vaja teha, et Citcoinist saaks tĂ€ieĂ”iguslik mĂŒntide ĂŒlekandmise vĂ”rk? VĂ€ga vĂ€he:

  • Konto loomise etapis rakendada privaatse/publikus vĂ”ti genereerimist. Privaatne vĂ”ti peab olema kasutaja kontol, publiku plokiahelas.
  • Teha mĂŒntide ĂŒlekandmine, kus kasutaja tuvastamiseks kasutatakse mitte nime, vaid publikust vĂ”tit.
  • KrĂŒpteerida tehingud, mis lĂ€hevad kasutajalt serverisse, tema privaatse vĂ”tmega.

KokkuvÔte

Meie oleme loonud Citcoin vĂ”rgu funktsioonidega: konto lisamine, saldo saamine, oma kontot tĂ€iendamine, mĂŒntide edastamine ĂŒhest kontost teise. Nii et mis maksis meile PoC ehitamine?

  • Tuleb uurida plokiahelat ĂŒldiselt ja Hyperledger Fabric'i eriti;
  • Õppida kasutama IBM vĂ”i Amazoni pilvi;
  • Selgeks saada programmeerimiskeel JS ja mĂ”ni veebiraamistik;
  • Kui mingid andmed tuleb salvestada mitte plokiahelasse, vaid eraldi andmebaasi, siis Ă”ppida integreeruma nĂ€iteks PostgreSQL-iga;
  • Ja viimane, kuid mitte vĂ€hem oluline — ilma Linuxi tundmisega ei pÀÀse tĂ€napĂ€eva maailmas kuhugi!)

Muidugi, ei ole see just rocket science, aga higi tuleb valada!

Allikakoode GitHubis

Allikakoode olen pannud GitHub. LĂŒhikirjeldus hoidlast:
Kataloog „server“ — Node.js server
Kataloog „client“ — Node.js klient
Kataloog „plokiahel“ (parameetrite vÀÀrtused ja vĂ”tmed, loomulikult, ei ole töötavad ja on toodud ainult nĂ€itena):

  • contract — nutilepingute algfail
  • wallet — kasutaja vĂ”tmed Hyperledger Fabric API kasutamiseks.
  • *.cds — kompileeritud versioonid nutilepingutest
  • *.json failid — nĂ€idised konfiguratsioonifailidest Hyperledger Fabric API kasutamiseks

See on alles algus!

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster