Blokoĉeno: kian PoC ni konstruu?

Viaj okuloj timas kaj viaj manoj jukas!

En antaŭaj artikoloj, ni traktis la teknologiojn sur kiuj blokĉenoj estas konstruitaj (Kion ni konstruu blokĉenon?) kaj kazoj kiuj povas esti efektivigitaj kun ilia helpo (Kial ni konstruu kazon?). Estas tempo labori per viaj manoj! Por efektivigi pilotojn kaj PoC (Pruvo de Koncepto), mi preferas uzi la nubojn, ĉar... ili estas alireblaj de ie ajn en la mondo kaj, ofte, ne necesas malŝpari tempon pri teda instalado de la medio, ĉar Estas antaŭfiksitaj agordoj. Do, ni faru ion simplan, ekzemple, reton por transdoni monerojn inter partoprenantoj kaj ni modeste nomu ĝin Bitcoin. Por tio ni uzos la IBM-nubon kaj la universalan blokĉenon Hyperledger Fabric. Unue, ni eltrovu kial Hyperledger Fabric nomiĝas universala blokĉeno?

Blokoĉeno: kian PoC ni konstruu?

Hyperledger Fabric - universala blokĉeno

Ĝenerale parolante, universala informsistemo estas:

  • Aro de serviloj kaj programara kerno, kiu plenumas komercan logikon;
  • Interfacoj por interago kun la sistemo;
  • Iloj por registriĝo, aŭtentigo kaj rajtigo de aparatoj/homoj;
  • Datumbazo stokanta funkciajn kaj arkivajn datumojn:

Blokoĉeno: kian PoC ni konstruu?

La oficiala versio de kio estas Hyperledger Fabric legeblas ĉe ejo, kaj mallonge, Hyperledger Fabric estas malfermfonta platformo, kiu permesas vin konstrui privatajn blokĉenojn kaj efektivigi arbitrajn inteligentajn kontraktojn skribitajn en la programlingvoj JS kaj Go. Ni rigardu detale la arkitekturon de Hyperledger Fabric kaj certigu, ke ĉi tio estas universala sistemo, kiu nur havas specifajn por stoki kaj registri datumojn. La specifeco estas, ke la datumoj, kiel en ĉiuj blokĉenoj, estas stokitaj en blokoj, kiuj estas metitaj sur la blokĉenon nur se la partoprenantoj atingas konsenton kaj post registrado la datumoj ne povas esti trankvile korektitaj aŭ forigitaj.

Hyperledger Ŝtofa Arkitekturo

La diagramo montras la Hyperledger Fabric-arkitekturon:

Blokoĉeno: kian PoC ni konstruu?

organizoj — organizoj enhavas kunulojn, t.e. blokĉeno ekzistas pro la subteno de organizoj. Malsamaj organizoj povas esti parto de la sama kanalo.

kanalo — logika strukturo, kiu kunigas samulojn en grupojn, t.e. la blokĉeno estas specifita. Hyperledger Fabric povas samtempe prilabori plurajn blokĉenojn kun malsama komerca logiko.

Provizanto de Membraj Servoj (MSP) estas CA (Atestila Aŭtoritato) por eldonado de identeco kaj asignado de roloj. Por krei nodon, vi devas interagi kun la MSP.

Samaj nodoj — kontroli transakciojn, stoki la blokĉenon, plenumi inteligentajn kontraktojn kaj interagi kun aplikoj. Samuloj havas identecon (cifereca atestilo), kiu estas eldonita fare de la MSP. Male al la reto Bitcoin aŭ Etherium, kie ĉiuj nodoj havas egalajn rajtojn, en Hyperledger Fabric nodoj ludas malsamajn rolojn:

  • Peer eble apoganta kunulo (EP) kaj plenumi inteligentajn kontraktojn.
  • Engaĝita kunulo (CP) - nur konservu datumojn en la blokĉeno kaj ĝisdatigu la "Mondan ŝtaton".
  • Ankro Peer (AP) - se pluraj organizoj partoprenas en la blokĉeno, tiam ankro-kunuloj estas uzataj por komunikado inter ili. Ĉiu organizo devas havi unu aŭ plurajn ankro-kunulojn. Uzante AP, ĉiu kunulo en organizo povas akiri informojn pri ĉiuj kunuloj en aliaj organizoj. Uzita por sinkronigi informojn inter AP-oj klaĉa protokolo.
  • Ĉefo Peer — se organizo havas plurajn samideanojn, tiam nur la gvidanto de la kunulo ricevos blokojn de la Mendoservo kaj donos ilin al la ceteraj samuloj. La gvidanto povas esti aŭ specifita statike aŭ selektita dinamike fare de kunuloj en la organizo. La klaĉprotokolo ankaŭ estas uzata por sinkronigi informojn pri gvidantoj.

aktivoj — estaĵoj kiuj havas valoron kaj estas stokitaj sur la blokĉeno. Pli specife, ĉi tio estas ŝlosilvaloraj datumoj en JSON-formato. Estas ĉi tiuj datumoj, kiuj estas registritaj en la Blockchain. Ili havas historion, kiu estas konservita en la blokĉeno, kaj aktuala stato, kiu estas konservita en la datumbazo "Monda ŝtato". Datumstrukturoj estas plenigitaj arbitre depende de komercaj taskoj. Ne estas postulataj kampoj, la sola rekomendo estas, ke aktivoj devas havi posedanton kaj esti valoraj.

Ledger — konsistas el la Blockchain kaj la Word-ŝtata datumbazo, kiu konservas la nunan staton de aktivoj. Monda ŝtato uzas LevelDB aŭ CouchDB.

Inteligenta kontrakto — uzante inteligentajn kontraktojn, la komerca logiko de la sistemo estas efektivigita. En Hyperledger Fabric, inteligentaj kontraktoj estas nomitaj ĉenkodo. Uzante ĉenkodon, aktivoj kaj transakcioj super ili estas specifitaj. En teknikaj terminoj, inteligentaj kontraktoj estas programaraj moduloj efektivigitaj en la programlingvoj JS aŭ Go.

Apogopolitiko — por ĉiu ĉenkodo, vi povas agordi politikon pri kiom da konfirmoj por transakcio estu atendataj kaj de kiu. Se la politiko ne estas agordita, tiam la defaŭlta estas: "la transakcio devas esti konfirmita de iu ajn membro de iu ajn organizo en la kanalo." Ekzemploj de politikoj:

  • La transakcio devas esti aprobita de iu administranto de la organizo;
  • Devas esti konfirmita de iu ajn membro aŭ kliento de la organizo;
  • Devas esti konfirmita de iu ajn samranga organizo.

Servo de mendo — pakas transakciojn en blokojn kaj sendas ilin al samuloj en la kanalo. Garantias liveron de mesaĝoj al ĉiuj samuloj en la reto. Uzita por industriaj sistemoj Kafka mesaĝmakleristo, por evoluo kaj testado solistoj.

CallFlow

Blokoĉeno: kian PoC ni konstruu?

  • La aplikaĵo komunikas kun Hyperledger Fabric uzante Go, Node.js aŭ Java SDK;
  • La kliento kreas tx-transakcion kaj sendas ĝin al aprobantaj samuloj;
  • La Samulo kontrolas la subskribon de la kliento, kompletigas la transakcion, kaj sendas la subtenon subskribon reen al la kliento. Ĉenkodo estas efektivigita nur sur la apoganta kunulo, kaj la rezulto de ĝia ekzekuto estas sendita al ĉiuj kunuloj. Ĉi tiu algoritmo de laboro estas nomita PBFT (Practical Byzantine Fault Tolerant) konsento. Diferencas de klasika BFT la fakto, ke la mesaĝo estas sendita kaj konfirmo estas atendita ne de ĉiuj partoprenantoj, sed nur de certa aro;
  • Post kiam la kliento ricevis la nombron da respondoj respondaj al la politiko de apogo, li sendas la transakcion al la Ordona servo;
  • La Mendado-servo generas blokon kaj sendas ĝin al ĉiuj kompromitantaj samuloj. Mendservo certigas sinsekvan registradon de blokoj, kiu forigas la tielnomitan ĉeflibroforkon (vidu sekcion "Forkoj");
  • Samuloj ricevas blokon, kontrolu la apogan politikon denove, skribu la blokon al la blokĉeno kaj ŝanĝu la staton en la DB "Monda ŝtato".

Tiuj. Tio rezultigas dividon de roloj inter la nodoj. Ĉi tio certigas, ke la blokĉeno estas skalebla kaj sekura:

  • Inteligentaj kontraktoj (ĉenkodo) plenumas apogajn kunulojn. Ĉi tio certigas la konfidencon de inteligentaj kontraktoj, ĉar ĝi ne estas konservita de ĉiuj partoprenantoj, sed nur de apogaj kunuloj.
  • Mendado devus funkcii rapide. Ĉi tio estas certigita de la fakto, ke Mendado nur formas blokon kaj sendas ĝin al fiksa aro de gvidaj samuloj.
  • Engaĝigaj kunuloj nur stokas la blokĉenon - povas esti multaj el ili kaj ili ne postulas multan potencon kaj tujan operacion.

Pliaj detaloj pri la arkitekturaj solvoj de Hyperledger Fabric kaj kial ĝi funkcias tiel kaj ne alie troveblas ĉi tie: Arkitekturo Originoj aŭ ĉi tie: Hyperledger Fabric: Distribuita Operaciumo por Permesitaj Blokĉenoj.

Do, Hyperledger Fabric estas vere universala sistemo per kiu vi povas:

  • Efektivigi arbitran komercan logikon uzante la inteligentan kontraktan mekanismon;
  • Registri kaj ricevi datumojn de la blokĉena datumbazo en formato JSON;
  • Donu kaj konfirmu API-aliron per Atestila Aŭtoritato.

Nun kiam ni komprenas iomete pri la specifaĵoj de Hyperledger Fabric, ni finfine faru ion utilan!

Deplojante blokĉenon

Formulado de la problemo

La tasko estas efektivigi la Citcoin-reton kun la sekvaj funkcioj: krei konton, akiri saldon, replenigi vian konton, translokigi monerojn de unu konto al alia. Ni desegnu objektomodelon, kiun ni plue efektivigos en inteligenta kontrakto. Do, ni havos kontojn, kiuj estas identigitaj per nomoj kaj enhavas bilancon, kaj liston de kontoj. Kontoj kaj listo de kontoj estas, laŭ Hyperledger Fabric-aktivaĵoj. Sekve, ili havas historion kaj nunan staton. Mi provos desegni ĉi tion klare:

Blokoĉeno: kian PoC ni konstruu?

La supraj figuroj estas la nuna stato, kiu estas konservita en la datumbazo "Monda ŝtato". Sub ili estas figuroj montrantaj la historion, kiu estas konservita en la blokĉeno. La nuna stato de aktivoj estas ŝanĝita per transakcioj. La Aktivo ŝanĝiĝas nur entute, do kiel rezulto de la transakcio, nova objekto estas kreita, kaj la nuna valoro de la valoraĵo eniras historion.

IBM Cloud

Ni kreas konton en IBM-nubo. Por uzi la blokĉenan platformon, ĝi devas esti ĝisdatigita al Pay-As-You-Go. Ĉi tiu procezo eble ne estas rapida, ĉar... IBM petas pliajn informojn kaj kontrolas ĝin permane. En pozitiva noto, mi povas diri, ke IBM havas bonajn trejnajn materialojn, kiuj permesas vin disfaldi Hyperledger Fabric en sia nubo. Mi ŝatis la jenajn artikolojn kaj ekzemplojn:

La jenaj estas ekrankopioj de la platformo IBM Blockchain. Ĉi tio ne estas instrukcio pri kiel krei blokĉenon, sed simple pruvo de la amplekso de la tasko. Do, por niaj celoj, ni faras unu Organizon:

Blokoĉeno: kian PoC ni konstruu?

Ni kreas nodojn en ĝi: Orderer CA, Org1 CA, Orderer Peer:

Blokoĉeno: kian PoC ni konstruu?

Ni kreas uzantojn:

Blokoĉeno: kian PoC ni konstruu?

Kreu Kanalon kaj nomu ĝin citcoin:

Blokoĉeno: kian PoC ni konstruu?

Esence Kanalo estas blokĉeno, do ĝi komenciĝas per bloko nulo (Geneza bloko):

Blokoĉeno: kian PoC ni konstruu?

Skribante Inteligentan Kontrakton

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

Intuicie, ĉio devus esti klara ĉi tie:

  • Estas pluraj funkcioj (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) kiujn la demo-programo nomos uzante la Hyperledger Fabric API.
  • La funkcioj SendFrom kaj RefillBalance generas Eventojn, kiujn ricevos la demoprogramo.
  • La instantiate funkcio estas vokita unufoje kiam inteligenta kontrakto estas instantiigita. Fakte, ĝi nomiĝas ne nur unufoje, sed ĉiufoje kiam la inteligenta kontrakto-versio ŝanĝiĝas. Tial pravalorigi liston kun malplena tabelo estas malbona ideo, ĉar Nun, kiam ni ŝanĝas la version de la inteligenta kontrakto, ni perdos la nunan liston. Sed estas bone, mi nur lernas).
  • Kontoj kaj listo de kontoj estas JSON-datumstrukturoj. JS estas uzata por manipulado de datumoj.
  • Vi povas akiri la aktualan valoron de valoraĵo per la funkcio getState, kaj ĝisdatigi ĝin per putState.
  • Kreante Konton, la funkcio AddAccount estas vokita, en kiu komparo estas farita por la maksimuma nombro da kontoj en la blokĉeno (maxAccounts = 5). Kaj ĉi tie estas jambo (ĉu vi rimarkis?), kiu kondukas al senfina pliiĝo de la nombro de kontoj. Tiaj eraroj devus esti evititaj)

Poste ni ŝarĝas la inteligentan kontrakton en la Kanalon kaj kreas ĝin:

Blokoĉeno: kian PoC ni konstruu?

Ni rigardu la transakcion por instali Smart Contract:

Blokoĉeno: kian PoC ni konstruu?

Ni rigardu la detalojn pri nia Kanalo:

Blokoĉeno: kian PoC ni konstruu?

Kiel rezulto, ni ricevas la sekvan diagramon de blokĉena reto en la IBM-nubo. La diagramo ankaŭ montras demoprogramon funkciantan en la Amazon-nubo sur virtuala servilo (pli pri ĝi en la sekva sekcio):

Blokoĉeno: kian PoC ni konstruu?

Krei GUI por Hyperledger Fabric API-vokoj

Hyperledger Fabric havas API kiu povas esti uzata por:

  • Krei kanalon;
  • Konektoj kunulo al kanalo;
  • Instalado kaj instantiigo de inteligentaj kontraktoj en la kanalo;
  • Voki transakciojn;
  • Petu informojn pri la blokĉeno.

Disvolviĝo de aplikaĵo

En nia demo-programo ni uzos la API nur por voki transakciojn kaj peti informojn, ĉar Ni jam kompletigis la ceterajn paŝojn uzante la IBM-blokĉeno-platformon. Ni skribas GUI uzante norman teknologian stakon: Express.js + Vue.js + Node.js. Vi povas skribi apartan artikolon pri kiel komenci krei modernajn TTT-aplikaĵojn. Ĉi tie mi lasos ligilon al la serio de prelegoj, kiujn mi plej ŝatis: Plena Stack Web App uzante Vue.js & Express.js. La rezulto estas kliento-servila aplikaĵo kun konata grafika interfaco en la Material Design-stilo de Guglo. La REST API inter kliento kaj servilo konsistas el pluraj vokoj:

  • HyperledgerDemo/v1/init - pravigu la blokĉenon;
  • HyperledgerDemo/v1/accounts/list — akiru liston de ĉiuj kontoj;
  • HyperledgerDemo/v1/account?name=Bob&balance=100 — krei Bob-konton;
  • HyperledgerDemo/v1/info?account=Bob — akiri informojn pri Bob-konto;
  • HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — translokigi du monerojn de Bob al Alice;
  • HyperledgerDemo/v1/disconnect - fermu la ligon al la blokĉeno.

Priskribo de la API kun ekzemploj inkluzivitaj en Retejo de leterportisto - konata programo por testi HTTP-API.

Demo-apliko en Amazon-nubo

Mi alŝutis la aplikaĵon al Amazon ĉar... IBM ankoraŭ ne povis ĝisdatigi mian konton kaj permesi al mi krei virtualajn servilojn. Kiel aldoni ĉerizon al la domajno: www.citcoin.info. Mi tenos la servilon ŝaltita dum kelka tempo, poste malŝaltos ĝin, ĉar... cendoj por luo gutas, kaj citcoin-moneroj ankoraŭ ne estas listigitaj en la borso) Mi inkluzivas ekrankopiojn de la demo en la artikolo por ke la logiko de la laboro estu klara. La demo-aplikaĵo povas:

  • Komencu la blokĉenon;
  • Kreu Konton (sed nun vi ne povas krei novan Konton, ĉar la maksimuma nombro da kontoj specifita en la inteligenta kontrakto estis atingita en la blokĉeno);
  • Ricevu liston de Kontoj;
  • Transloku citcoin-monerojn inter Alice, Bob kaj Alex;
  • Ricevu eventojn (sed nun ne ekzistas maniero montri eventojn, do por simpleco, la interfaco diras, ke eventoj ne estas subtenataj);
  • Ensalutu agojn.

Unue ni pravigas la blokĉenon:

Blokoĉeno: kian PoC ni konstruu?

Poste, ni kreas nian konton, ne perdu tempon kun la saldo:

Blokoĉeno: kian PoC ni konstruu?

Ni ricevas liston de ĉiuj disponeblaj kontoj:

Blokoĉeno: kian PoC ni konstruu?

Ni elektas la sendinton kaj ricevanton, kaj ricevas iliajn saldojn. Se la sendinto kaj la ricevanto estas la samaj, tiam lia konto estos replenigita:

Blokoĉeno: kian PoC ni konstruu?

En la protokolo ni kontrolas la plenumadon de transakcioj:

Blokoĉeno: kian PoC ni konstruu?

Fakte, tio estas ĉio kun la demo-programo. Malsupre vi povas vidi nian transakcion en la blokĉeno:

Blokoĉeno: kian PoC ni konstruu?

Kaj la ĝenerala listo de transakcioj:

Blokoĉeno: kian PoC ni konstruu?

Kun ĉi tio, ni sukcese kompletigis la efektivigon de la PoC por krei la Citcoin-reton. Kion alian oni devas fari por ke Citcoin fariĝu plenrajta reto por translokado de moneroj? Tre malmulte:

  • En la etapo de kreado de konto, efektivigu la generacion de privata / publika ŝlosilo. La privata ŝlosilo devas esti stokita kun la konta uzanto, la publika ŝlosilo devas esti stokita en la blokĉeno.
  • Faru monertransigo, en kiu publika ŝlosilo, prefere ol nomo, estas uzata por identigi la uzanton.
  • Ĉifri transakciojn irantajn de la uzanto al la servilo per sia privata ŝlosilo.

konkludo

Ni efektivigis la Citcoin-reton kun la sekvaj funkcioj: aldoni konton, akiri saldon, plenigi vian konton, translokigi monerojn de unu konto al alia. Do, kion kostis al ni konstrui PoC?

  • Vi devas studi blockchain ĝenerale kaj Hyperledger Fabric aparte;
  • Lernu uzi IBM aŭ Amazon-nubojn;
  • Lernu la programlingvon JS kaj iun retan kadron;
  • Se iuj datumoj devas esti konservitaj ne en la blokĉeno, sed en aparta datumbazo, tiam lernu integri, ekzemple, kun PostgreSQL;
  • Kaj laste sed ne malpli - vi ne povas vivi en la moderna mondo sen scio pri Linukso!)

Kompreneble, ĝi ne estas raketscienco, sed vi devos multe labori!

Fontoj en GitHub

Fontoj surmetitaj GitHub. Mallonga priskribo de la deponejo:
Katalogo «servilo» — Node.js-servilo
Katalogo «kliento» — Node.js-kliento
Katalogo «blockchain"(parametrovaloroj kaj ŝlosiloj, kompreneble, ne funkcias kaj estas donitaj nur kiel ekzemplo):

  • contract — inteligenta kontrakto fontkodo
  • monujo - uzantaj ŝlosiloj por uzi la Hyperledger Fabric API.
  • *.cds - kompilitaj versioj de inteligentaj kontraktoj
  • *.json dosieroj - ekzemploj de agordaj dosieroj por uzi la Hyperledger Fabric API

Estas nur la komenco!

fonto: www.habr.com

Aldoni komenton