Blockchain: hvilken PoC skal vi bygge?

Øynene dine er redde og hendene dine klør!

I tidligere artikler har vi behandlet teknologiene som blokkjeder er bygget på (Hva skal vi bygge en blokkjede?) og saker som kan implementeres med deres hjelp (Hvorfor skal vi bygge en sak?). Det er på tide å jobbe med hendene! For å implementere piloter og PoC (Proof of Concept), foretrekker jeg å bruke skyene, fordi... de kan nås fra hvor som helst i verden, og ofte er det ikke nødvendig å kaste bort tid på kjedelig installasjon av miljøet, fordi Det er forhåndsinnstilte konfigurasjoner. Så, la oss lage noe enkelt, for eksempel et nettverk for overføring av mynter mellom deltakere og la oss beskjedent kalle det Bitcoin. Til dette vil vi bruke IBM-skyen og den universelle blokkjeden Hyperledger Fabric. Først, la oss finne ut hvorfor Hyperledger Fabric kalles en universell blokkjede?

Blockchain: hvilken PoC skal vi bygge?

Hyperledger Fabric - en universell blokkjede

Generelt sett er et universelt informasjonssystem:

  • Et sett med servere og en programvarekjerne som utfører forretningslogikk;
  • Grensesnitt for interaksjon med systemet;
  • Verktøy for registrering, autentisering og autorisasjon av enheter/personer;
  • Database som lagrer drifts- og arkivdata:

Blockchain: hvilken PoC skal vi bygge?

Den offisielle versjonen av hva Hyperledger Fabric er kan leses på nettsted, og kort fortalt er Hyperledger Fabric en åpen kildekode-plattform som lar deg bygge private blokkjeder og utføre vilkårlige smarte kontrakter skrevet i programmeringsspråkene JS og Go. La oss se i detalj på arkitekturen til Hyperledger Fabric og sørge for at dette er et universelt system som kun har spesifikasjoner for lagring og registrering av data. Spesifisiteten er at dataene, som i alle blokkjeder, lagres i blokker som plasseres på blokkjeden kun dersom deltakerne oppnår enighet og etter registrering av dataene ikke stille kan korrigeres eller slettes.

Hyperledger Fabric Architecture

Diagrammet viser Hyperledger Fabric-arkitekturen:

Blockchain: hvilken PoC skal vi bygge?

Organisasjoner — organisasjoner inneholder jevnaldrende, dvs. blockchain eksisterer på grunn av støtte fra organisasjoner. Ulike organisasjoner kan være en del av samme kanal.

Kanal — en logisk struktur som forener jevnaldrende i grupper, dvs. blokkjeden er spesifisert. Hyperledger Fabric kan behandle flere blokkjeder samtidig med forskjellig forretningslogikk.

Leverandør av medlemstjenester (MSP) er en CA (Certificate Authority) for utstedelse av identitet og tildeling av roller. For å opprette en node må du samhandle med MSP.

Peer noder — verifisere transaksjoner, lagre blokkjeden, utfør smarte kontrakter og samhandle med applikasjoner. Kolleger har en identitet (digitalt sertifikat), som utstedes av MSP. I motsetning til Bitcoin- eller Etherium-nettverket, hvor alle noder har like rettigheter, spiller Fabric-noder forskjellige roller i Hyperledger:

  • Peer kanskje støtter jevnaldrende (EP) og utføre smarte kontrakter.
  • Forpliktende jevnaldrende (CP) - lagre kun data i blokkjeden og oppdater "verdenstilstanden".
  • Anker Peer (AP) - hvis flere organisasjoner deltar i blokkjeden, så brukes ankerfeller for kommunikasjon mellom dem. Hver organisasjon må ha en eller flere ankerkolleger. Ved å bruke AP kan enhver fagfelle i en organisasjon få informasjon om alle jevnaldrende i andre organisasjoner. Brukes til å synkronisere informasjon mellom AP-er sladderprotokoll.
  • Leder Peer — hvis en organisasjon har flere likemenn, er det kun lederen av fagfellen som vil motta blokker fra Bestillingstjenesten og gi dem til resten av jevnaldrende. Lederen kan enten spesifiseres statisk eller velges dynamisk av jevnaldrende i organisasjonen. Sladderprotokollen brukes også til å synkronisere informasjon om ledere.

Eiendeler — enheter som har verdi og er lagret på blokkjeden. Mer spesifikt er dette nøkkelverdidata i JSON-format. Det er disse dataene som registreres i Blockchain. De har en historie, som er lagret i blokkjeden, og en nåværende tilstand, som er lagret i "World state"-databasen. Datastrukturer fylles ut vilkårlig avhengig av forretningsoppgaver. Det er ingen obligatoriske felter, den eneste anbefalingen er at eiendeler må ha en eier og være verdifulle.

Ledger — består av Blockchain og Word-statusdatabasen, som lagrer gjeldende status for eiendeler. Verdensstaten bruker LevelDB eller CouchDB.

Smart kontrakt — ved hjelp av smarte kontrakter implementeres forretningslogikken til systemet. I Hyperledger Fabric kalles smarte kontrakter kjedekode. Ved å bruke kjedekode spesifiseres eiendeler og transaksjoner over dem. Teknisk sett er smarte kontrakter programvaremoduler implementert i programmeringsspråkene JS eller Go.

Retningslinjer for godkjenning — for hver kjedekode kan du sette en policy på hvor mange bekreftelser for en transaksjon som skal forventes og fra hvem. Hvis policyen ikke er angitt, er standarden: "transaksjonen må bekreftes av ethvert medlem av en hvilken som helst organisasjon i kanalen." Eksempler på retningslinjer:

  • Transaksjonen må godkjennes av enhver administrator i organisasjonen;
  • Må bekreftes av ethvert medlem eller klient av organisasjonen;
  • Må bekreftes av enhver likemannsorganisasjon.

Bestillingstjeneste — pakker transaksjoner i blokker og sender dem til jevnaldrende i kanalen. Garanterer levering av meldinger til alle jevnaldrende på nettverket. Brukes til industrielle systemer Kafka meldingsmegler, for utvikling og testing Bare.

CallFlow

Blockchain: hvilken PoC skal vi bygge?

  • Applikasjonen kommuniserer med Hyperledger Fabric ved hjelp av Go, Node.js eller Java SDK;
  • Klienten oppretter en tx-transaksjon og sender den til støttende kolleger;
  • Peer verifiserer klientens signatur, fullfører transaksjonen og sender påtegningssignaturen tilbake til klienten. Kjedekode utføres kun på den støttende peeren, og resultatet av dens utførelse sendes til alle peers. Denne arbeidsalgoritmen kalles PBFT (Practical Byzantine Fault Tolerant) konsensus. Skiller seg fra klassisk BFT det faktum at meldingen sendes og bekreftelse forventes ikke fra alle deltakere, men bare fra et visst sett;
  • Etter at klienten har mottatt antall svar som tilsvarer påtegningspolicyen, sender han transaksjonen til bestillingstjenesten;
  • Bestillingstjenesten genererer en blokk og sender den til alle forpliktende kolleger. Bestillingstjeneste sikrer sekvensiell registrering av blokker, noe som eliminerer den såkalte hovedbokgaffelen (se avsnittet "Gaffler");
  • Peers mottar en blokk, sjekk godkjenningspolicyen på nytt, skriv blokken til blokkjeden og endre tilstanden i "World state" DB.

De. Dette resulterer i en rollefordeling mellom nodene. Dette sikrer at blokkjeden er skalerbar og sikker:

  • Smarte kontrakter (kjedekode) utfører godkjenning av jevnaldrende. Dette sikrer konfidensialiteten til smarte kontrakter, fordi den lagres ikke av alle deltakerne, men kun av å støtte jevnaldrende.
  • Bestillingen skal fungere raskt. Dette sikres ved at Bestilling kun danner en blokk og sender den til et fast sett med lederkolleger.
  • Forpliktende jevnaldrende lagrer bare blokkjeden – det kan være mange av dem og de krever ikke mye kraft og umiddelbar drift.

Flere detaljer om de arkitektoniske løsningene til Hyperledger Fabric og hvorfor det fungerer på denne måten og ikke ellers finner du her: Arkitekturs opprinnelse eller her: Hyperledger Fabric: Et distribuert operativsystem for tillatte blokkjeder.

Så Hyperledger Fabric er et virkelig universelt system som du kan:

  • Implementer vilkårlig forretningslogikk ved å bruke den smarte kontraktsmekanismen;
  • Ta opp og motta data fra blokkjededatabasen i JSON-format;
  • Gi og verifiser API-tilgang ved hjelp av sertifiseringsinstans.

Nå som vi forstår litt om detaljene til Hyperledger Fabric, la oss endelig gjøre noe nyttig!

Utplassering av blockchain

Formulering av problemet

Oppgaven er å implementere Citcoin-nettverket med følgende funksjoner: opprette en konto, få en saldo, fylle på kontoen din, overføre mynter fra en konto til en annen. La oss tegne en objektmodell, som vi skal implementere videre i en smart kontrakt. Så vi vil ha kontoer som er identifisert med navn og inneholder en saldo og en liste over kontoer. Kontoer og en liste over kontoer er, når det gjelder Hyperledger Fabric-eiendeler. Følgelig har de en historie og en nåværende tilstand. Jeg skal prøve å tegne dette tydelig:

Blockchain: hvilken PoC skal vi bygge?

De øverste tallene er gjeldende tilstand, som er lagret i "World state"-databasen. Under dem er det figurer som viser historien som er lagret i blokkjeden. Den nåværende tilstanden til eiendeler endres av transaksjoner. Eiendelen endres bare som en helhet, så som et resultat av transaksjonen opprettes et nytt objekt, og den nåværende verdien av eiendelen går inn i historien.

IBM Cloud

Vi oppretter en konto i IBM sky. For å bruke blockchain-plattformen må den oppgraderes til Pay-As-You-Go. Denne prosessen er kanskje ikke rask, fordi... IBM ber om tilleggsinformasjon og verifiserer den manuelt. På en positiv måte kan jeg si at IBM har godt opplæringsmateriell som lar deg distribuere Hyperledger Fabric i deres sky. Jeg likte følgende serie med artikler og eksempler:

Følgende er skjermbilder av IBM Blockchain-plattformen. Dette er ikke en instruksjon om hvordan du lager en blokkjede, men bare en demonstrasjon av oppgavens omfang. Så for våre formål lager vi én organisasjon:

Blockchain: hvilken PoC skal vi bygge?

Vi lager noder i den: Orderer CA, Org1 CA, Orderer Peer:

Blockchain: hvilken PoC skal vi bygge?

Vi oppretter brukere:

Blockchain: hvilken PoC skal vi bygge?

Lag en kanal og kall den citcoin:

Blockchain: hvilken PoC skal vi bygge?

I hovedsak er Channel en blokkjede, så den starter med blokk null (Genesis-blokk):

Blockchain: hvilken PoC skal vi bygge?

Skrive en smart 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;

Intuitivt burde alt være klart her:

  • Det er flere funksjoner (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) som demoprogrammet kaller ved hjelp av Hyperledger Fabric API.
  • SendFrom- og RefillBalance-funksjonene genererer hendelser som demoprogrammet vil motta.
  • Instantieringsfunksjonen kalles én gang når en smart kontrakt blir instansiert. Faktisk kalles det ikke bare én gang, men hver gang den smarte kontraktsversjonen endres. Derfor er initialisering av en liste med en tom matrise en dårlig idé, fordi Nå, når vi endrer versjonen av den smarte kontrakten, vil vi miste den gjeldende listen. Men det er greit, jeg bare lærer).
  • Kontoer og en liste over kontoer er JSON-datastrukturer. JS brukes til datamanipulering.
  • Du kan få gjeldende verdi av en eiendel ved å bruke getState-funksjonskallet, og oppdatere den ved å bruke putState.
  • Når du oppretter en konto, kalles AddAccount-funksjonen opp, der det gjøres en sammenligning for maksimalt antall kontoer i blokkjeden (maxAccounts = 5). Og her er det en jamb (har du lagt merke til det?), som fører til en endeløs økning i antall kontoer. Slike feil bør unngås)

Deretter laster vi den smarte kontrakten inn i kanalen og instansierer den:

Blockchain: hvilken PoC skal vi bygge?

La oss se på transaksjonen for installasjon av Smart Contract:

Blockchain: hvilken PoC skal vi bygge?

La oss se på detaljene om kanalen vår:

Blockchain: hvilken PoC skal vi bygge?

Som et resultat får vi følgende diagram av et blokkjedenettverk i IBM-skyen. Diagrammet viser også et demoprogram som kjører i Amazon-skyen på en virtuell server (mer om det i neste avsnitt):

Blockchain: hvilken PoC skal vi bygge?

Opprette en GUI for Hyperledger Fabric API-kall

Hyperledger Fabric har et API som kan brukes til å:

  • Lag kanal;
  • Tilkoblinger peer-to-kanal;
  • Installasjon og instansiering av smarte kontrakter i kanalen;
  • Ringe transaksjoner;
  • Be om informasjon om blokkjeden.

Applikasjonsutvikling

I vårt demoprogram vil vi bare bruke API til å ringe transaksjoner og be om informasjon, fordi Vi har allerede fullført de resterende trinnene ved å bruke IBM blockchain-plattformen. Vi skriver en GUI ved å bruke en standard teknologistabel: Express.js + Vue.js + Node.js. Du kan skrive en egen artikkel om hvordan du begynner å lage moderne webapplikasjoner. Her vil jeg legge igjen en lenke til serien med foredrag som jeg likte best: Full Stack Web App med Vue.js & Express.js. Resultatet er en klient-server-applikasjon med et kjent grafisk grensesnitt i Googles Material Design-stil. REST API mellom klient og server består av flere kall:

  • HyperledgerDemo/v1/init - initialiser blokkjeden;
  • HyperledgerDemo/v1/accounts/list — få en liste over alle kontoer;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 — opprett Bob-konto;
  • HyperledgerDemo/v1/info?account=Bob — få informasjon om Bob-kontoen;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 - overfør to mynter fra Bob til Alice;
  • HyperledgerDemo/v1/disconnect - lukk forbindelsen til blokkjeden.

Beskrivelse av API med eksempler inkludert i Postman nettsted - et velkjent program for testing av HTTP API.

Demoapplikasjon i Amazon-skyen

Jeg lastet opp applikasjonen til Amazon fordi... IBM har fortsatt ikke vært i stand til å oppgradere kontoen min og tillate meg å lage virtuelle servere. Slik legger du til et kirsebær på domenet: www.citcoin.info. Jeg vil holde serveren på en stund, og deretter slå den av, fordi... cent til leie drypper, og citcoin-mynter er ennå ikke børsnotert) Jeg inkluderer skjermbilder av demoen i artikkelen slik at logikken i arbeidet er klar. Demoapplikasjonen kan:

  • Initialiser blokkjeden;
  • Opprett en konto (men nå kan du ikke opprette en ny konto, fordi det maksimale antallet kontoer spesifisert i den smarte kontrakten er nådd i blokkjeden);
  • Motta en liste over kontoer;
  • Overfør citcoin-mynter mellom Alice, Bob og Alex;
  • Motta hendelser (men nå er det ingen måte å vise hendelser på, så for enkelhets skyld sier grensesnittet at hendelser ikke støttes);
  • Logg handlinger.

Først initialiserer vi blokkjeden:

Blockchain: hvilken PoC skal vi bygge?

Deretter oppretter vi kontoen vår, ikke kast bort tid med saldoen:

Blockchain: hvilken PoC skal vi bygge?

Vi får en liste over alle tilgjengelige kontoer:

Blockchain: hvilken PoC skal vi bygge?

Vi velger avsender og mottaker, og får deres saldo. Hvis avsender og mottaker er de samme, vil kontoen hans fylles på:

Blockchain: hvilken PoC skal vi bygge?

I loggen overvåker vi utførelsen av transaksjoner:

Blockchain: hvilken PoC skal vi bygge?

Egentlig er det alt med demoprogrammet. Nedenfor kan du se transaksjonen vår i blokkjeden:

Blockchain: hvilken PoC skal vi bygge?

Og den generelle listen over transaksjoner:

Blockchain: hvilken PoC skal vi bygge?

Med dette har vi fullført implementeringen av PoC for å opprette Citcoin-nettverket. Hva mer må gjøres for at Citcoin skal bli et fullverdig nettverk for overføring av mynter? Veldig lite:

  • På kontoopprettingsstadiet implementerer du genereringen av en privat/offentlig nøkkel. Den private nøkkelen må lagres hos kontobrukeren, den offentlige nøkkelen må lagres i blokkjeden.
  • Foreta en myntoverføring der en offentlig nøkkel, i stedet for et navn, brukes til å identifisere brukeren.
  • Krypter transaksjoner som går fra brukeren til serveren med hans private nøkkel.

Konklusjon

Vi har implementert Citcoin-nettverket med følgende funksjoner: legge til en konto, få en saldo, fylle på kontoen din, overføre mynter fra en konto til en annen. Så, hva kostet det oss å bygge en PoC?

  • Du må studere blockchain generelt og Hyperledger Fabric spesielt;
  • Lær å bruke IBM- eller Amazon-skyer;
  • Lær JS-programmeringsspråket og noe nettrammeverk;
  • Hvis noen data må lagres ikke i blokkjeden, men i en egen database, så lær å integrere for eksempel med PostgreSQL;
  • Og sist men ikke minst - du kan ikke leve i den moderne verden uten kunnskap om Linux!)

Selvfølgelig er det ikke rakettvitenskap, men du må jobbe hardt!

Kilder på GitHub

Kilder satt på GitHub. Kort beskrivelse av depotet:
Katalog «server» — Node.js-server
Katalog «kunde» — Node.js-klient
Katalog «blockchain"(parameterverdier og nøkler fungerer selvfølgelig ikke og gis bare som et eksempel):

  • kontrakt — kildekode for smart kontrakt
  • lommebok — brukernøkler for bruk av Hyperledger Fabric API.
  • *.cds - kompilerte versjoner av smarte kontrakter
  • *.json-filer - eksempler på konfigurasjonsfiler for bruk av Hyperledger Fabric API

Det er bare begynnelsen!

Kilde: www.habr.com

Legg til en kommentar