Blockchain: ce ne costă să construim un PoC?

Ochi se tem, dar mâinile mă gândesc să lucreze!

În articolele anterioare, am discutat despre tehnologiile pe care se bazează blockhain-urile (Ce ne-ar costa să construim un blockchain?) și cazurile care pot fi realizate cu ajutorul lor (Ce ne-ar costa să construim un caz?). A venit momentul să trecem la treabă! Pentru realizarea pilotilor și PoC (Proof of Concept), prefer să folosesc cloud-uri, deoarece acestea sunt accesibile din orice colț al lumii și, adesea, nu este necesar să pierd timp cu instalarea plictisitoare a mediului, deoarece există configurații preinstalate. Așadar, să facem ceva simplu, de exemplu, o rețea pentru transferul de monede între participanți și să o numim modest Ccoin. Pentru aceasta, vom folosi cloud-ul IBM și blockchain-ul versatil Hyperledger Fabric. La început, să înțelegem de ce Hyperledger Fabric este numit blockchain versatil?

Blockchain: ce ne costă să construim un PoC?

Hyperledger Fabric — blockchain versatil

Dacă vorbim în general, un sistem informațional versatil este:

  • Un set de servere și un nucleu software care execută logica de afaceri;
  • Interfețe pentru interacțiunea cu sistemul;
  • Instrumente pentru înregistrarea, autentificarea și autorizarea dispozitivelor/oamenilor;
  • O bază de date care stochează date operaționale și arhivate:

Blockchain: ce ne costă să construim un PoC?

Despre ce este Hyperledger Fabric se poate citi în versiunea oficială pe site, dar pe scurt, Hyperledger Fabric este o platformă opensource, care permite construirea de blockchain-uri private și executarea de contracte inteligente scrise în limbaje de programare JS și Go. Să aruncăm o privire detaliată asupra arhitecturii Hyperledger Fabric și să ne asigurăm că este un sistem versatil, în care doar există specificități legate de stocarea și înregistrarea datelor. Specificitatea constă în faptul că datele, la fel ca în toate blockchain-urile, sunt stocate în blocuri, care sunt introduse în blockchain doar dacă participanții au ajuns la un consens, iar după înregistrare, datele nu pot fi modificate sau șterse fără să se observe.

Arhitectura Hyperledger Fabric

În diagramă este prezentată arhitectura Hyperledger Fabric:

Blockchain: ce ne costă să construim un PoC?

Organizații — organizațiile conțin peer-uri, astfel încât blockchain-ul există datorită sprijinului organizațiilor. Diferite organizații pot face parte din același canal.

Canal — o structură logică care grupează peer-urile, astfel fiind stabilit blockchain-ul. Hyperledger Fabric poate procesa simultan mai multe blockchain-uri cu logici de afaceri diferite.

Provider de Servicii de Membru (MSP) — este CA (Autoritate de Certificare) pentru emiterea identității și desemnarea rolurilor. Pentru a crea un nod, trebuie să interactuezi cu MSP.

Noduri peer — verifică tranzacțiile, stochează blockchain-ul, execută contracte inteligente și interacționează cu aplicațiile. Nodurile peer au o identitate (certificat digital), emisă de MSP. Spre deosebire de rețelele Bitcoin sau Ethereum, unde toate nodurile sunt egale, în Hyperledger Fabric nodurile joacă roluri diferite:

  • Un nod peer poate fi nodată endorsing peer (EP) și poate executa contracte inteligente.
  • Committing peer (CP) — doar stochează date în blockchain și actualizează „World state”.
  • Anchor Peer (AP) — dacă în blockchain participă mai multe organizații, nodurile anchor sunt folosite pentru a face legătura între ele. Fiecare organizație trebuie să aibă unul sau mai multe noduri anchor. Prin intermediul AP, orice nod dintr-o organizație poate obține informații despre toate nodurile din alte organizații. Pentru sincronizarea informațiilor între AP se folosește protocolul gossip.
  • Leader Peer — dacă o organizație are mai multe noduri, doar nodul lider va primi blocuri din serviciul de ordonare și le va distribui celorlalte noduri. Liderul poate fi stabilit static sau ales dinamic de nodurile din organizație. Pentru sincronizarea informațiilor despre lideri, de asemenea, se utilizează protocolul gossip.

Active — entități care au valoare și care sunt stocate în blockchain. Mai precis — sunt date de tip key-value în format JSON. Aceste date sunt cele care sunt înregistrate în blockchain „Blockchain”. Ele au o istorie stocată în blockchain și o stare curentă, care este stocată în baza de date „World state”. Structurile de date sunt umplute aleatoriu în funcție de obiectivele de afaceri. Nu există câmpuri obligatorii, singura recomandare este ca activele să aibă un proprietar și să reprezinte o valoare.

Registru — constă în blockchain „Blockchain” și baza de date „World state”, în care este stocată starea actuală a activelor. World state folosește LevelDB sau CouchDB.

Contract inteligent — prin intermediul contractelor inteligente se implementează logica de afaceri a sistemului. În Hyperledger Fabric, contractele inteligente se numesc chaincode. Prin chaincode se definesc activele și tranzacțiile pe care le implică. Vorbind tehnic, contractele inteligente sunt module software implementate în limbaje de programare JS sau Go.

Politica de aprobat — pentru fiecare chaincode se poate defini politici privind câte și de la cine sunt necesare aprobările pentru o tranzacție. Dacă politica nu este definită, în mod implicit se folosește: „tranzacția trebuie să fie aprobată de orice membru al oricărei organizații din canal”. Exemple de politici:

  • Transacția trebuie să fie confirmată de către orice administrator al organizației;
  • Trebuie să fie confirmată de orice membru sau client al organizației;
  • Trebuie să fie confirmată de orice peer al organizației.

Serviciul de Ordering — împachetează tranzacțiile în blocuri și le trimite peer-urilor în canal. Asigură livrarea mesajelor tuturor peer-urilor din rețea. Pentru sistemele industriale se folosește brokerul de mesaje Kafka, pentru dezvoltare și testare Solo.

CallFlow

Blockchain: ce ne costă să construim un PoC?

  • Aplicația interacționează cu Hyperledger Fabric, utilizând SDK-uri Go, Node.js sau Java;
  • Clientul creează tranzacția tx și o trimite către peer-urile de endorsare;
  • Peer-ul verifică semnătura clientului, execută tranzacția și trimite semnătura de endorsare înapoi clientului. Chaincode-urile se execută doar pe peer-urile de endorsare, iar rezultatul execuției este distribuit tuturor peer-urilor. Acest algoritm de funcționare se numește — consens PBFT (Practical Byzantine Fault Tolerant). Se deosebește de classica BFT prin faptul că mesajul este trimis și se așteaptă confirmarea nu de la toți participanții, ci doar de la un set specific;
  • După ce clientul primește numărul de răspunsuri corespunzător politicii de endorsare, trimite tranzacția la serviciul de Ordering;
  • Serviciul de Ordering formează blocul și îl trimite tuturor peer-urilor de committing. Serviciul de Ordering asigură înregistrarea secvențială a blocurilor, ceea ce exclude, așa-zisul, ledger fork (vezi secțiunea „Forks”);
  • Peer-urile primesc blocul, verifică din nou politica de endorsare, înregistrează blocul în blockchain și schimbă starea în baza de date „World state”.

Adică, se obține o separare a rolurilor între noduri. Aceasta asigură scalabilitatea și securitatea blockchain-ului:

  • Smart contractele (chaincode) sunt executate de peer-urile de endorsare. Aceasta asigură confidențialitatea smart contractelor, deoarece ele nu sunt stocate de toți participanții, ci doar pe peer-urile de endorsare.
  • Ordering trebuie să funcționeze rapid. Acest lucru este asigurat prin faptul că Ordering formează doar blocul și îl trimite unui set fix de peer-uri lider.
  • Committing peers doar stochează blockchainul — pot fi multe și nu necesită putere mare de procesare și lucru instantaneu.

Mai multe detalii despre soluțiile arhitecturale Hyperledger Fabric și de ce funcționează așa, nu altfel, pot fi consultate aici: Origins Architecture sau aici: Hyperledger Fabric: A Distributed Operating System for Permissioned Blockchains.

Așadar, Hyperledger Fabric este cu adevărat un sistem versatil, cu ajutorul căruia se poate:

  • Implementa orice logică de afaceri, utilizând mecanismul smart contractelor;
  • Scrieți și obțineți date dintr-o bază de date blockchain în format JSON;
  • Furnizați și verificați accesul la API, folosind Autoritatea de Certificare.

Acum, că am înțeles un pic despre specificul Hyperledger Fabric, să facem în sfârșit ceva util!

Desfășurăm blockchain-ul

Formularea problemei

Sarcina este să implementăm o rețea Citcoin cu următoarele funcții: crearea unui cont, obținerea soldului, alimentarea contului, transferul de monede de la un cont la altul. Vom schița un model obiect care va fi implementat ulterior în contractul inteligent. Așadar, vom avea conturi care se identifică prin nume (name) și conțin un sold (balance), și o listă de conturi. Conturile și lista de conturi sunt, în termeni de Hyperledger Fabric, active (assets). Prin urmare, acestea au o istorie și un stadiu curent. Voi încerca să ilustrez acest lucru:

Blockchain: ce ne costă să construim un PoC?

Figurile de sus reprezintă stadiul curent, care este stocat în baza de date „World state”. Sub ele sunt figurile care ilustrează istoria, care este stocată în blockchain. Stadiul curent al activelor se schimbă prin tranzacții. Activele se schimbă doar în întregime, astfel încât, în urma executării tranzacției, se creează un nou obiect, iar valoarea curentă a activului va merge în istorie.

Cloud IBM

Creăm un cont în cloudul IBM. Pentru a utiliza platforma blockchain, aceasta trebuie să fie actualizată la Pay-As-You-Go. Acest proces poate să nu fie rapid, deoarece IBM cere informații suplimentare și le verifică manual. Pe partea pozitivă, pot spune că IBM are materialele de învățare destul de bune, care permit desfășurarea Hyperledger Fabric în cloud-ul lor. Mi-a plăcut următorul ciclu de articole și exemple:

Mai jos sunt capturi de ecran ale platformei Blockchain IBM. Acesta nu este un ghid pentru crearea blockchain-ului, ci doar o demonstrație a amploarei sarcinii. Așadar, pentru scopurile noastre, vom crea o Organizație:

Blockchain: ce ne costă să construim un PoC?

În aceasta vom crea noduri: Orderer CA, Org1 CA, Orderer Peer:

Blockchain: ce ne costă să construim un PoC?

Creăm utilizatori:

Blockchain: ce ne costă să construim un PoC?

Creează un Canal și numește-l citcoin:

Blockchain: ce ne costă să construim un PoC?

Practic, Canalul este blockchain-ul, așa că începe cu blocul zero (Genesis block):

Blockchain: ce ne costă să construim un PoC?

Scriem Smart Contract

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

Intuitiv, ar trebui să fie totul clar aici:

  • Există câteva funcții (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) care vor fi apelate de programul demo prin intermediul API-ului Hyperledger Fabric.
  • Funcțiile SendFrom și RefillBalance generează evenimente (Event) pe care programul demo le va primi.
  • Funcția instantiate este apelată o singură dată la instanțierea unui contract inteligent. De fapt, ea este apelată nu o singură dată, ci de fiecare dată când versiunea contractului inteligent se schimbă. Prin urmare, inițializarea listei cu un array gol este o idee proastă, deoarece acum, când schimbăm versiunea contractului inteligent, vom pierde lista curentă. Dar nimic, eu doar învăț).
  • Account-urile și lista account-urilor (accounts) sunt structuri de date JSON. Pentru manipularea datelor se folosește JS.
  • Pentru a obține valoarea curentă a asset-ului, se poate utiliza apelul funcției getState, iar pentru a actualiza se folosește putState.
  • La crearea unui Account se apelează funcția AddAccount, în care se compară numărul maxim de account-uri în blockchain (maxAccounts = 5). Și aici este o eroare (ați observat?), care duce la o creștere infinită a numărului de account-uri. Astfel de erori trebuie evitate)

Apoi, încărcăm contractul inteligent în Channel și îl instanțiem:

Blockchain: ce ne costă să construim un PoC?

Privim tranzacția de instalare a Smart Contract-ului:

Blockchain: ce ne costă să construim un PoC?

Privim detalii despre Channel-ul nostru:

Blockchain: ce ne costă să construim un PoC?

Ca rezultat, obținem următoarea schemă a rețelei blockchain în cloud-ul IBM. De asemenea, în schemă se află un program demo, care rulează în cloud-ul Amazon pe un server virtual (detalii despre el vor fi în următoarea secțiune):

Blockchain: ce ne costă să construim un PoC?

Crearea GUI pentru apelarea API-ului Hyperledger Fabric

Hyperledger Fabric dispune de un API, care poate fi utilizat pentru:

  • Crearea de channel;
  • Conectarea peer-ului la channel;
  • Instalarea și instanțierea contractelor inteligente în channel;
  • Apelarea tranzacțiilor;
  • Solicitarea de informații în blockchain.

Dezvoltarea aplicației

În programul nostru demo, vom folosi API-ul doar pentru apelarea tranzacțiilor și solicitarea de informații, deoarece celelalte etape au fost deja realizate, utilizând platforma blockchain IBM. Scriem GUI, folosind stiva standard de tehnologii: Express.js + Vue.js + Node.js. Despre cum să începi să creezi aplicații web moderne se poate scrie un articol separat. Aici voi lăsa un link către seria de cursuri, care mi-a plăcut cel mai mult: Full Stack Web App using Vue.js & Express.js. Ca rezultat, a rezultat o aplicație client-server cu o interfață grafică familiară în stil Material Design de la Google. REST API-ul între client și server constă din mai multe apeluri:

  • HyperledgerDemo/v1/init — inițializează blockchain-ul;
  • HyperledgerDemo/v1/accounts/list — obține lista tuturor account-urilor;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 — creează account-ul Bob;
  • HyperledgerDemo/v1/info?account=Bob — obține informații despre account-ul Bob;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — transfer două monede de la Bob la Alice;
  • HyperledgerDemo/v1/disconnect — închide conexiunea cu blockchain-ul.

Am pus descrierea API-ului cu exemple pe site-ul „Postman” — un program bine cunoscut pentru testarea API-urilor HTTP.

Aplicația demo în cloud-ul Amazon

Am încărcat aplicația pe Amazon, deoarece IBM încă nu a reușit să îmi actualizeze contul și să permită crearea de servere virtuale. Ca o cherry on top, am atașat domeniul: www.citcoin.info. Voi menține serverul activ pentru o vreme, după care îl voi opri, deoarece costurile de închiriere continuă să se adune, iar monedele citcoin de pe bursă nu sunt încă cotate) În articol voi include capturi de ecran ale demo-ului, pentru a ilustra logica de funcționare. Aplicația demo poate:

  • Inițializa blockchain-ul;
  • Crea un cont (dar acum nu se poate crea un cont nou, deoarece s-a atins numărul maxim de conturi prevăzut în smart contract);
  • Obține lista conturilor;
  • Transfera monede citcoin între Alice, Bob și Alex;
  • Obține evenimente (dar acum nu se pot afișa evenimente, așa că în interfață pentru simplificare scrie că evenimentele nu sunt suportate);
  • Loghează acțiunile.

Mai întâi inițializăm blockchain-ul:

Blockchain: ce ne costă să construim un PoC?

Apoi ne creăm contul, fără să ne zgârci la balanță:

Blockchain: ce ne costă să construim un PoC?

Obținem lista tuturor conturilor disponibile:

Blockchain: ce ne costă să construim un PoC?

Alegem expeditorul și destinatarul, obținem soldurile lor. Dacă expeditorul și destinatarul sunt aceleași, soldul său va fi alimentat:

Blockchain: ce ne costă să construim un PoC?

În log urmărim executarea tranzacțiilor:

Blockchain: ce ne costă să construim un PoC?

Și cu aplicația demo cam atât. Apoi putem verifica tranzacția noastră în blockchain:

Blockchain: ce ne costă să construim un PoC?

Și lista generală a tranzacțiilor:

Blockchain: ce ne costă să construim un PoC?

Astfel am finalizat cu succes implementarea PoC pentru crearea rețelei Citcoin. Ce mai trebuie făcut pentru ca Citcoin să devină o rețea complet funcțională pentru transferul de monede? Foarte puțin:

  • În etapa de creare a contului, să implementăm generarea de chei private/publice. Cheia privată trebuie păstrată de utilizatorul contului, cheia publică în blockchain.
  • Să realizăm transferul de monede, în care pentru identificarea utilizatorului se utilizează cheia publică, nu numele.
  • Să criptăm tranzacțiile care vin de la utilizator la server cu cheia sa privată.

Concluzie

Am implementat rețeaua Citcoin cu funcții: adaugă cont, obține sold, alimentează-ți contul, transferă monede de la un cont la altul. Așadar, cât de mult ne-a costat construirea PoC-ului?

  • Trebuie să studiem blockchain-ul în general și Hyperledger Fabric în special;
  • Să ne învățăm să folosim cloud-urile IBM sau Amazon;
  • Învățarea limbajului de programare JS și a unui framework web;
  • Dacă trebuie să stocăm anumite date nu în blockchain, ci într-o bază de date separată, atunci trebuie să învățăm să ne integrăm, de exemplu, cu PostgreSQL;
  • Și ultimul pe listă, dar nu și ca importanță — fără cunoștințe de Linux în lumea modernă nu ai șanse!)

Desigur, nu e rocket science, dar va fi nevoie de muncă!

Sursa pe GitHub

Am pus sursele pe GitHub. Descriere scurtă a depozitului:
Catalogul „server” — server Node.js
Catalogul „client” — client Node.js
Catalogul „blockchain” (valorile parametrilor și cheile, desigur, nu sunt funcționale și sunt date doar ca exemplu):

  • contract — sursa contractului inteligent
  • wallet — cheile utilizatorului pentru utilizarea API-ului Hyperledger Fabric.
  • *.cds — versiunile compilate ale contractelor inteligente
  • *.json fișiere — exemple de fișiere de configurare pentru utilizarea API-ului Hyperledger Fabric

Este doar începutul!

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster