Acis baidās, bet rokas niez!
Iepriekšējās rakstos mēs izpētījām tehnoloģijas, uz kurām balstās blokķēdes () un gadījumus, kurus iespējams īstenot ar to palīdzību (). Ir pienācis laiks strādāt ar rokām! Lai realizētu pilotprojektus un PoC (Proof of Concept), es dodu priekšroku izmantot mākoni, jo pie tam ir pieejams no jebkuras vietas pasaulē, un bieži vien nav jātērē laiks garlaicīgai vides uzstādīšanai, jo ir iepriekš konfigurētas sistēmas. Tātad, apskatīsim kaut ko vienkāršu, piemēram, tīklu monētu pārsūtīšanai starp dalībniekiem, ko nosauksim pieticīgi par Citoins. Šim nolūkam izmantosim IBM mākoni un universālo blokķēdi Hyperledger Fabric. Vispirms izpētīsim, kāpēc Hyperledger Fabric dēvē par universālo blokķēdi?

Hyperledger Fabric — universāla blokķēde
Ja runājam vispārīgi, tad universāla informācijas sistēma ir:
- Serveru kopums un programmatūras kodols, kas izpilda biznesa loģiku;
- Interfeisi mijiedarbībai ar sistēmu;
- Rīki ierīču / cilvēku reģistrēšanai, autentificēšanai un autorizēšanai;
- Datu bāze, kas glabā operatīvos un arhivētos datus:

Oficiālo versiju par to, kas ir Hyperledger Fabric, var izlasīt , bet īsumā, Hyperledger Fabric — ir opensource platforma, kas ļauj veidot slēgtas blokķēdes un veikt jebkādus viedos līgumus, kas uzrakstīti programmēšanas valodās JS un Go. Apskatīsim detalizēti Hyperledger Fabric arhitektūru un pārliecināsimies, ka tā ir universāla sistēma, kurā pastāv specifika datu glabāšanā un ierakstē. Šī specifika ir tāda, ka dati, kā visās blokķēdēs, tiek glabāti blokos, kuri tiek ievietoti blokķēdē tikai tad, ja dalībnieki ir nonākuši pie konsensa, un pēc ierakstīšanas datus nav iespējams nemanāmi labot vai dzēst.
Hyperledger Fabric arhitektūra
Shēmā attēlota Hyperledger Fabric arhitektūra:

Organizācijas — organizācijas satur peer’us, tādējādi blokķēde pastāv, pateicoties organizāciju atbalstam. Atšķirīgas organizācijas var pievienoties vienam kanālam.
Channel — loģiskā struktūra, kas apvieno peer’us grupās, tādējādi nosakot blokķēdi. Hyperledger Fabric var vienlaikus apstrādāt vairākas blokķēdes ar atšķirīgām biznesa loģikām.
Membership Services Provider (MSP) — tas ir CA (Certificate Authority), kas izsniedz identitātes un piešķir lomas. Lai izveidotu mezglu, jāsadarbojas ar MSP.
Peer mezgli — pārbauda darījumus, glabā blokķēdi, izpilda viedos līgumus un mijiedarbojas ar lietotnēm. Peer-iem ir identitāte (digitālais sertifikāts), ko izdod MSP. Atšķirībā no Bitcoin vai Etherium tīkla, kur visas mezgls ir līdzvērtīgi, Hyperledger Fabric mezgli ieņem dažādas lomas:
- Peer var būt apstiprinošais peer (EP) un izpildīt viedos līgumus.
- Apstiprinošais peer (CP) — tikai glabā datus blokķēdē un aktualizē „World state”.
- Ankura Peer (AP) — ja blokķēdē piedalās vairākas organizācijas, tad ankoru peer-iem izmanto, lai sazinātos starp tām. Katrai organizācijai jābūt vienam vai vairākiem ankoru peer. Ar AP jebkurš peer organizācijā var saņemt informāciju par visiem peer-iem citās organizācijās. Lai sinhronizētu informāciju starp AP, tiek izmantots .
- Līdera Peer — ja organizācijai ir vairāki peer-iem, tad tikai līdera peer saņems blokos no Ordering service un nodos tos pārējiem peer-iem. Līderi var noteikt gan statiski, gan izvēlēties dinamiski organizācijas peer-iem. Lai sinhronizētu informāciju par līderiem, arī tiek izmantots gossipa protokols.
Aktīvi — vienības, kas ir vērtīgas un tiek glabātas blokķēdē. Precīzāk — tās ir key-value dati JSON formātā. Tieši šie dati tiek ierakstīti blokķēdē „Blockchain”. Tiem ir vēsture, kas tiek glabāta blokķēdē, un pašreizējais stāvoklis, kas tiek glabāts „World state” datu bāzē. Datu struktūras piepildās brīvi atkarībā no biznesa uzdevumiem. Nav nekādu obligātu lauku, vienīgā rekomendācija — aktīviem jābūt īpašniekam un jābūt vērtīgām.
Grāmata — sastāv no blokķēdes „Blockchain” un datu bāzes „World state”, kurā tiek glabāts pašreizējais aktīvu stāvoklis. World state izmanto LevelDB vai CouchDB.
Viedais līgums — ar viedajiem līgumiem tiek īstenota sistēmas biznesa loģika. Hyperledger Fabric viedie līgumi tiek saukti par chaincode. Ar chaincode tiek noteikti aktīvi un darījumi ar tiem. Tehniskā valodā sakot, viedie līgumi ir programmatūras moduļi, kas izstrādāti JS vai Go programmēšanas valodās.
Apstiprināšanas politika — katram chaincode var noteikt politikas, cik daudz un no kā nepieciešams sagaidīt apstiprinājumus darījumam. Ja politika nav noteikta, tad pēc noklusējuma tiek izmantots: “darījumu jāapstiprina jebkuram dalībniekam (member) no jebkuras organizācijas kanālā”. Politiku piemēri:
- Transakciju jāapstiprina jebkuram organizācijas administratoram;
- To var apstiprināt jebkurš organizācijas loceklis vai klients;
- To jāapstiprina jebkuram organizācijas peer.
Pasūtīšanas pakalpojums — iesaiņo transakcijas blokos un nosūta peer am kanālā. Garantē ziņojumu piegādi visiem peer am tīklā. Rūpnieciskajām sistēmām tiek izmantots , izstrādei un testēšanai .
CallFlow

- Lietotne mijiedarbojas ar Hyperledger Fabric, izmantojot Go, Node.js vai Java SDK;
- Klients izveido transakciju tx un nosūta to uz apstiprinājuma peer am;
- Peer pārbauda klienta parakstu, veic transakciju un nosūta apstiprinājuma parakstu atpakaļ klientam. Chaincode tiek izpildīta tikai apstiprinājuma peer, un izpildes rezultāts tiek izplatīts visiem peer am. Šis darba algoritms tiek saukts par — PBFT (Practical Byzantine Fault Tolerant) konsensu. Tas atšķiras no tas, ka ziņojums tiek izplatīts un tiek gaidīts apstiprinājums nevis no visiem dalībniekiem, bet tikai no noteikta kopuma;
- Pēc tam, kad klients saņēmis atbildes skaitu, kas atbilst apstiprinājuma politikai, viņš nosūta transakciju uz Pasūtīšanas pakalpojumu;
- Pasūtīšanas pakalpojums veido bloku un nosūta to visiem apstiprināšanas peer am. Pasūtīšanas pakalpojums nodrošina secīgu bloku ierakstu, tādējādi izslēdzot tā dēvēto, ledger fork ();
- Peer saņem bloku, vēlreiz pārbauda apstiprinājuma politiku, ieraksta bloku blokķēdē un maina stāvokli «World state» DB.
T.i., tiek izveidota lomu sadalīšana starp mezgliem. Tas nodrošina blokķēdes mērogojamību un drošību:
- Viedie līgumi (chaincode) tiek izpildīti apstiprinājuma peer. Tas nodrošina smarta kontraktu konfidencialitāti, jo tie netiek glabāti pie visiem dalībniekiem, bet tikai apstiprinājuma peer os.
- Pasūtījumam jāstrādā ātri. To nodrošina tas, ka Pasūtīšana tikai veido bloku un nosūta to noteiktai grupai līdera peer.
- Apstiprinājuma peer tikai glabā blokķēdi — to var būt daudz un tiem nav nepieciešama liela jauda vai nekavējoša darbība.
Detalizētāk ar arhitektūras risinājumiem Hyperledger Fabric un kāpēc tas darbojas tā, nevis citādi var apskatīt šeit: vai šeit: .
Tātad, Hyperledger Fabric ir patiesi universāla sistēma, ar kuru var:
- Realizēt pēc pasūtījuma izstrādātu biznesa loģiku, izmantojot viedlīgumu mehānismu;
- Reģistrēt un iegūt datus no blokķēdes datubāzes JSON formātā;
- Nodrošināt un pārbaudīt piekļuvi API, izmantojot Sertifikācijas iestādi.
Tagad, kad esam nedaudz iepazinušies ar Hyperledger Fabric specifiku, sāksim beidzot kaut ko noderīgu!
Izveidojam blokķēdi
Uzdevuma formulēšana
Uzdevums ir īstenot Citcoin tīklu ar šādām funkcijām: izveidot kontu, iegūt bilanci, papildināt kontu, pārsūtīt monētas no vienas konta uz citu. Uzzīmēsim objektu modeli, ko vēlāk īstenosim viedo līgumu. Tātad mums būs konti, kas tiek identificēti ar nosaukumiem (name) un satur bilanci (balance), un kontu saraksts. Konti un kontu saraksts — Hyperledger Fabric terminoloģijā ir aktīvi (assets). Tādejādi tiem ir vēsture un pašreizējais stāvoklis. Mēģināšu to vizuāli attēlot:

Augšējās figūras attēlo pašreizējo stāvokli, kas tiek glabāts „World state”. Zem tām ir figūras, kas parāda vēsturi, kas tiek glabāta blokķēdē. Aktīvu pašreizējais stāvoklis tiek mainīts ar darījumiem. Aktīvs tiek mainīts tikai kopumā, tādēļ transaction veikšanas rezultātā tiek izveidots jauns objekts, un pašreizējā aktīva vērtība nonāk vēsturē.
IBM mākoņpakalpojums
Izveidojam kontu . Lai izmantotu blokķēdes platformu, tai ir jāveic atjauninājums uz Pay-As-You-Go. Šis process var būt lēns, jo IBM pieprasa papildu informāciju un to pārbauda manuāli. No pozitīvās puses, varu teikt, ka IBM ir labi mācību materiāli, kas ļauj izveidot Hyperledger Fabric savā mākoņpakalpojumā. Man patika šis rakstu un piemēru cikls:
Tālāk ir sniegtas IBM Blokķēdes platformas ekrānuzņēmumi. Tas nav rokasgrāmata blokķēdes izveidošanai, bet tikai uzdevuma apjoma demonstrācija. Tātad mūsu mērķiem izveidojam vienu organizāciju:

Izveidojam mezglus: Orderer CA, Org1 CA, Orderer Peer:

Izveidojam lietotājus:

Izveidojam kanālu un nosaucam to par citcoin:

Patiess kanāls ir blokķēde, tāpēc tas sākas ar nulles bloku (Genesis block):

Rakstām viedo līgumu
/*
* 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;
Cik intuitīvi tas jābūt skaidram:
- Pastāv vairākas funkcijas (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance), kuras demo programma izsauks, izmantojot Hyperledger Fabric API.
- Funkcijas SendFrom un RefillBalance ģenerē notikumus (Event), kurus demo programma saņems.
- Instantiēšanas funkcija tiek izsaukta vienu reizi, kad tiek izveidots viedais līgums. Patiesībā to izsauc nevis vienu reizi, bet katru reizi, kad tiek mainīta viedā līguma versija. Tādēļ saraksta inicializēšana ar tukšu masīvu ir slikta ideja, jo tagad, mainot viedā līguma versiju, mēs pazaudēsim esošo sarakstu. Bet nekas, es tikai mācos).
- Accounti un accountu saraksts (accounts) ir JSON datu struktūras. Datu manipulācijai tiek izmantots JS.
- Iegūt pašreizējo asset vērtību var ar getState funkcijas izsaukumu, bet atjaunināt to ar putState.
- Izveidei Account tiek izsaukta AddAccount funkcija, kurā tiek salīdzināts maksimālais account skaits blokķēdē (maxAccounts = 5). Un šeit ir kļūda (pamanījāt?), kas noved pie bezgalīga account skaita pieauguma. Šādām kļūdām jāizvairās)
Tālāk mēs augšupielādējam viedo līgumu kanālā un to instancējam:

Skatāmies darījumu viedā līguma uzstādīšanai:

Skatāmies detaļas par mūsu kanālu:

Rezultātā iegūstam šādu blokķēdes tīkla shēmu IBM mākoņa vidē. Shēmā ir arī demo programma, kas darbojas Amazon mākoņī uz virtuālā servera (par to sīkāk nākamajā sadaļā):

GUI izveide Hyperledger Fabric API izsaukumiem
Hyperledger Fabric ir API, ko var izmantot:
- Lai izveidotu kanālu;
- Lai pieslēgtu peer kanālam;
- Lai instalētu un instancētu viedos līgumus kanālā;
- Lai izsauktu darījumus;
- Lai pieprasītu informāciju blokķēdē.
Pieteikuma izstrāde
Mūsu demo programmā mēs izmantosim API tikai darījumu izsaukšanai un informācijas pieprasīšanai, jo citus soļus mēs jau esam veikuši, izmantojot IBM blokķēdes platformu. Rakstīsim GUI, izmantojot standarta tehnoloģiju kaudzi: Express.js + Vue.js + Node.js. Par to, kā uzsākt mūsdienīgu tīmekļa pieteikumu izveidi, varētu uzrakstīt atsevišķu rakstu. Šeit atstāšu saiti uz lekciju sēriju, kas man visvairāk patika: . Rezultātā izveidojām klient-server lietojumprogrammu ar pazīstamu grafisko saskarni, kas atbilst Google Materiāla dizainam. REST API starp klientu un serveri sastāv no vairākiem izsaukumiem:
- HyperledgerDemo/v1/init — inicializēt blokķēdi;
- HyperledgerDemo/v1/accounts/list — iegūt visu accountu sarakstu;
- HyperledgerDemo/v1/account?name=Bob&balance=100 — izveidot Bob account;
- HyperledgerDemo/v1/info?account=Bob — iegūt informāciju par Bob account;
- HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — pārsūtīt divas monētas no Bob uz Alice;
- HyperledgerDemo/v1/disconnect — pārtraukt savienojumu ar blokķēdi.
API apraksts ar piemēriem ir pieejams — plaši pazīstamajā programmā HTTP API testēšanai.
Demonstrācijas lietojumprogramma Amazon mākoņī
Lietojumprogrammu augšupielādēju Amazon, jo IBM joprojām nav varējusi atjaunināt manu kontu un ļaut izveidot virtuālos serverus. Kā vīksnu pievienoju domēnu: . Atstāsšu serveri uz nedaudz laika ieslēgtu, pēc tam izslēgšu, jo nomas centi krājas, bet citcoin monētas biržā vēl netiek kotētas) Ierakstā ievietošu ekrānuzņēmumus no demonstrācijas, lai parādītu darba loģiku. Demonstrācijas lietojumprogramma var:
- Iesākt blokķēdi;
- Izveidot kontu (bet šobrīd jaunu kontu nevar izveidot, jo blokķēdē ir sasniegts maksimālais kontu skaits, kas noteikts viedajā kontraktā);
- Iegūt visu kontu sarakstu;
- Pārsūtīt citcoin monētas starp Alice, Bob un Alex;
- Saņemt notikumus (bet šobrīd notikumus nav iespējams parādīt, tāpēc interfeisā vienkāršības labad ir norādīts, ka notikumi netiek atbalstīti);
- Žurnālu darbības.
Vispirms mēs iesākat blokķēdi:

Pēc tam izveidojam savu kontu, nekaunējamies no bilances:

Iegūstam visu pieejamo kontu sarakstu:

Izvēlamies sūtītāju un saņēmēju, iegūstam viņu bilances. Ja sūtītājs un saņēmējs ir viens un tas pats, tad tiks papildināta viņa konta bilance:

Žurnālā sekojam līdzi darījumu izpildei:

Par pašu demonstrācijas programmu ir viss. Tālāk varam apskatīt mūsu darījumu blokķēdē:

Un kopējo darījumu sarakstu:

Ar to mēs veiksmīgi pabeidzām PoC par Citcoin tīkla izveidi. Ko vēl jādara, lai Citcoin kļūtu par pilnvērtīgu valūtas pārskaitījumu tīklu? Ļoti maz:
- Account veidošanas posmā ieviest privātā/publiskā atslēgas ģenerēšanu. Privātajai atslēgai jāuzglabājas lietotāja accountā, publiskajai — blokķēdē.
- Veikt monētu pārskaitījumus, kur identificēšanai tiek izmantota ne vārds, bet publiskā atslēga.
- Šifrēt darījumus, kas nāk no lietotāja uz serveri, izmantojot viņa privāto atslēgu.
Secinājums
Mēs esam izveidojuši Citcoin tīklu ar funkcijām: pievienot kontu, iegūt bilanci, papildināt savu kontu, pārsūtīt monētas no viena konta uz citu. Tātad, kas mums izmaksāja PoC izveidi?
- Nepieciešams izpētīt blokķēdi kopumā un Hyperledger Fabric it īpaši;
- Iemācīties izmantot IBM vai Amazon mākoņus;
- Iemācīties programmēšanas valodu JS un kādu web ietvaru;
- Ja kādi dati jāuzglabā nevis blokķēdē, bet atsevišķā datu bāzē, tad iemācīties integrēties, piemēram, ar PostgreSQL;
- Un pēdējais sarakstā, bet ne pēc nozīmības — bez Linux zināšanām mūsdienu pasaulē nekur nav iespējams!)
Protams, tas nav nekas sarežģīts, bet būs jāstrādā!
Avota kodi GitHub
Avoti novietoti uz . Īss repo apraksts:
Katalogs «serveris» — Node.js serveris
Katalogs «klients» — Node.js klients
Katalogs «blockchain» (parametru vērtības un atslēgas, protams, nav funkcionējošas un ir norādītas tikai piemēram):
- contract — viedā līguma avots
- wallet — lietotāja atslēgas Hyperledger Fabric API izmantošanai.
- *.cds — kompilētās viedlīgumu versijas
- *.json faili — piemēru konfigurācijas faili Hyperledger Fabric API izmantošanai
Tas ir tikai sākums!
Avots: habr.com
