The eyes are afraid, but the hands are itching!
In previous articles, we explored the technologies on which blockchains are built () and the cases that can be implemented with their help (). It's time to work with our hands! For implementing pilots and PoCs (Proof of Concept), I prefer to use clouds because they are accessible from anywhere in the world, and often, there's no need to waste time on tedious environment setup as there are pre-installed configurations. So, let's create something simple, like a network for transferring coins between participants and modestly call it Citcoin. We will use IBM Cloud and the universal blockchain Hyperledger Fabric. First, let's understand why Hyperledger Fabric is called a universal blockchain.

Hyperledger Fabric — a universal blockchain
In general terms, a universal information system is:
- A set of servers and a software core that performs business logic;
- Interfaces for interacting with the system;
- Means for registering, authenticating, and authorizing devices/people;
- A database that stores operational and archived data:

The official version of what Hyperledger Fabric is can be read on the , and in short, Hyperledger Fabric is an open-source platform that allows building private blockchains and executing arbitrary smart contracts written in programming languages JS and Go. Let's take a closer look at the architecture of Hyperledger Fabric and confirm that this is a universal system, where there are specific details regarding data storage and recording. The specifics are that data, like in all blockchains, is stored in blocks that are added to the blockchain only if participants reach a consensus, and after recording, the data cannot be quietly modified or deleted.
Architecture of Hyperledger Fabric
The diagram shows the architecture of Hyperledger Fabric:

Organizations — organizations contain peers, thus the blockchain exists thanks to the support of organizations. Different organizations can join one channel.
Channel — a logical structure that groups peers, thus defining the blockchain. Hyperledger Fabric can simultaneously process several blockchains with different business logic.
Membership Services Provider (MSP) — это CA (Certificate Authority) для выдачи identity и назначения ролей. Для создания ноды нужно взаимодействовать с MSP.
Peer nodes — проверяют транзакции, хранят блокчейн, выполняют смарт-контракты и взаимодействуют с приложениями. У peer-ов есть identity (цифровой сертификат), который выдает MSP. В отличие от сети Bitcoin или Etherium, где все ноды равноправны, в Hyperledger Fabric ноды играют разные роли:
- Peer может быть endorsing peer (EP) и выполнять смарт-контракты.
- Committing peer (CP) — только сохраняют данные в блокчейне и актуализируют «World state».
- Anchor Peer (AP) — если в блокчейне участвуют несколько организаций, то анкор peer-ы используются для связи между ними. Каждая организация должна иметь один или несколько анкор peer. С помощью AP любой peer в организации может получить информацию о всех peer-ах в других организациях. Для синхронизации информации между AP используется .
- Leader Peer — если организация имеет несколько peer-ов, то только лидер peer будет получать блоки из Ordering service и отдавать их остальным peer-ам. Лидер может как задаваться статически, так и выбираться динамически peer-ами в организации. Для синхронизации информации о лидерах также используется gossip протокол.
Assets — сущности, имеющие ценность, которые хранятся в блокчейне. Более конкретно — это key-value данные в формате JSON. Именно эти данные и записываются в блокчейн «Blockchain». У них есть история, которая хранится в блокчейне и текущее состояние, которое хранится в базе данных «World state». Структуры данных наполняются произвольно в зависимости от бизнес задач. Нет никаких обязательных полей, единственная рекомендация — asset-ы должны иметь владельца и представлять ценность.
Ledger — состоит из блокчейна «Blockchain» и базы данных «Word state», в которой хранится текущее состояние asset-ов. World state использует LevelDB или CouchDB.
Smart contract — с помощью смарт-контрактов реализуется бизнес логика системы. В Hyperledger Fabric смарт-контракты называются chaincode. С помощью chaincode задаются asset-ы и транзакции над ними. Если говорить техническим языком, то смарт-контракты — это программные модули, реализованные на языках программирования JS или Go.
Endorsement policy Za vsak chaincode lahko določite politike, koliko in od katerih članov je potrebno potrditi transakcijo. Če politika ni določena, se privzeto uporablja: "transakcijo mora potrditi katerikoli član katere koli organizacije v channelu". Primeri politik:
- Transakcijo mora potrditi kateri koli administrator organizacije;
- Mora potrditi kateri koli član ali stranka organizacije;
- Mora potrditi kateri koli peer organizacije.
Storitve razporejanja — pakira transakcije v bloke in jih pošilja peer-om v channel. Zagotavlja dostavo sporočil vsem peer-om v omrežju. Za industrijske sisteme se uporablja , za razvoj in testiranje .
CallFlow

- Aplikacija komunicira s Hyperledger Fabric z uporabo Go, Node.js ali Java SDK;
- Stranka ustvari transakcijo tx in jo pošlje na peer-e, ki potrjujejo;
- Peer preveri podpis stranke, izvede transakcijo in pošlje potrditveno podpisovanje nazaj stranki. Chaincode se izvaja le na peer-ih, ki potrjujejo, rezultat njegovega izvajanja pa se pošlje vsem peer-om. Ta algoritem dela se imenuje — PBFT (Practical Byzantine Fault Tolerant) konsenz. Razlikuje se od po tem, da se sporočilo razpošlje in pričakuje potrditev ne od vseh udeležencev, ampak samo od določenega nabora;
- Ko stranka prejme število odgovorov, ki ustrezajo politiki potrjevanja, pošlje transakcijo na storitev razporejanja;
- Storitve razporejanja oblikujejo blok in ga pošljejo vsem committing peer-om. Storitve razporejanja zagotavljajo dosledno zapisovanje blokov, kar izključuje tako imenovano 'ledger fork' ();
- Peer-ji prejmejo blok, še enkrat preverijo politiko potrjevanja, zapišejo blok v blockchain in spremenijo stanje v "World state" DB.
To pomeni, da pride do delitve vlog med vozlišči. To zagotavlja razširljivost in varnost blockchaina:
- Pametne pogodbe (chaincode) izvajajo peers, ki potrjujejo. To zagotavlja zaupnost pametnih pogodb, saj se shranijo samo pri peers, ki potrjujejo, ne pri vseh udeležencih.
- Razporejanje mora delovati hitro. To je zagotovljeno, ker razporejanje samo oblikuje blok in ga pošlje na fiksno število leader peer-ov.
- Committing peers samo shranjujejo blockchain — teh je lahko veliko in ne potrebujejo velike moči ter takojšnje delovanja.
Več o arhitekturnih rešitvah Hyperledger Fabric in zakaj deluje tako, kot deluje, si lahko ogledate tukaj: ali tukaj: .
Dakle, Hyperledger Fabric je zaista univerzalna platforma koja omogućava:
- Implementiranje poslovne logike pomoću pametnih ugovora;
- Zapisivanje i dobijanje podataka iz blokčejn baze podataka u JSON formatu;
- Obezbeđivanje i verifikacija pristupa API-ju korišćenjem Certificate Authority.
Sada kada smo malo bolje razumeli suštinu Hyperledger Fabric, hajde da napokon uradimo nešto korisno!
Implementacija blokčejna
Definisanje zadatka
Zadatak je implementirati Citcoin mrežu sa sledećim funkcijama: kreirati račun, dobiti stanje, dopuniti saldo, prebaciti novčiće sa jednog računa na drugi. Napravimo objektni model koji ćemo kasnije implementirati u pametan ugovor. Dakle, imaćemo račune koji se identifikuju imenima (name) i sadrže saldo (balance), kao i listu računa. Računi i lista računa su, u terminologiji Hyperledger Fabric, sredstva (assets). Imaju svoju istoriju i trenutno stanje. Pokušaću da to slikovito prikažem:

Gornje figure predstavljaju trenutno stanje koje se čuva u 'World state' bazi. Ispod njih su figure koje pokazuju istoriju koja se čuva u blokčejnu. Trenutno stanje sredstava menja se transakcijama. Sredstvo se menja samo u celini, stoga se kao rezultat izvršenja transakcije kreira novi objekat, a trenutno stanje sredstva se prebacuje u istoriju.
IBM Cloud
Otvorite račun u . Da biste koristili platformu za blokčejn, morate je nadograditi na Pay-As-You-Go model. Ovaj proces može biti spor, jer IBM traži dodatne informacije i ručno ih proverava. Sa pozitivne strane, mogu reći da IBM ima dobre obrazovne materijale koji omogućavaju implementaciju Hyperledger Fabric u njihovom oblaku. Svideo mi se sledeći niz članaka i primera:
U nastavku su prikazane slike iz IBM Blockchain platforme. Ovo nije uputstvo za kreiranje blokčejna, već samo demonstracija obima zadatka. Dakle, za naše potrebe pravimo jednu Organizaciju:

U njoj kreiramo čvorove: Orderer CA, Org1 CA, Orderer Peer:

Otvori korisnike:

Kreiramo Channel i nazivamo ga citcoin:

Suštinski, Channel je blokčejn, stoga počinje od nultog bloka (Genesis block):

Pišemo pametni ugovor
/*
* 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;
Intuitivno bi ovde sve trebalo biti jasno:
- Obstaja več funkcij (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance), ki jih bo demo program klical preko Hyperledger Fabric API.
- Funkciji SendFrom in RefillBalance generirata dogodke (Event), ki jih bo prejel demo program.
- Funkcija instantiate se pokliče enkrat ob instanciranju pametne pogodbe. V resnici pa se pokliče večkrat ob spremembi verzije pametne pogodbe. Zato je inicializacija seznama z praznim poljem slaba ideja, saj ob spremembi verzije pametne pogodbe izgubimo trenutni seznam. Ampak nič, le učim se).
- Računi in seznam računov (accounts) so JSON strukture podatkov. Za manipulacijo podatkov se uporablja JS.
- Trenutno vrednost sredstva lahko pridobimo s klicem funkcije getState, za posodobitev pa uporabimo putState.
- Ob ustvarjanju računa se pokliče funkcija AddAccount, kjer se izvede primerjava z največjim številom računov v blockchainu (maxAccounts = 5). In tukaj je napaka (ste opazili?), ki vodi v neskončno rast števila računov. Takšnih napak se je treba izogibati)
Nato naložimo pametno pogodbo v Channel in jo instanciramo:

Poglejmo transakcijo za namestitev pametne pogodbe:

Poglejmo podrobnosti o našem Channel:

Kot rezultat dobimo naslednjo shemo blockchain omrežja v oblaku IBM. Shema prav tako prikazuje demo program, ki teče v oblaku Amazon na virtualnem strežniku (podrobneje o tem v naslednjem razdelku):

Ustvarjanje GUI za klice Hyperledger Fabric API
Hyperledger Fabric ima API, ki se lahko uporablja za:
- Ustvarjanje channel-a;
- Povezovanje peer-a s channel-om;
- Namestitev in instanciranje pametnih pogodb v channel-u;
- Klicanje transakcij;
- Zahtevanje informacij iz blockchaina.
Razvoj aplikacije
V našem demo programu bomo uporabljali API samo za klicanje transakcij in zahtevo informacij, saj smo ostale korake že izvedli z uporabo blockchain platforme IBM. Pisali bomo GUI z uporabo standardnega tehnološkega sklada: Express.js + Vue.js + Node.js. O tem, kako začeti izdelovati sodobne spletne aplikacije, bi lahko napisali poseben članek. Tukaj dodajam povezavo na serijo predavanj, ki so mi bila najbolj všeč: . V rezultatu je nastala odjemalsko-strežniška aplikacija z znanim grafičnim vmesnikom v slogu Material Design od Google. REST API med odjemalcem in strežnikom sestavljajo številni klici:
- HyperledgerDemo/v1/init — inicializirati blockchain;
- HyperledgerDemo/v1/accounts/list — pridobiti seznam vseh računov;
- HyperledgerDemo/v1/account?name=Bob&balance=100 — ustvariti račun Bob;
- HyperledgerDemo/v1/info?account=Bob — pridobiti informacije o računu Bob;
- HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — prenesti dve kovanci od Boba k Alici;
- HyperledgerDemo/v1/disconnect — prekiniti povezavo z blokovno verigo.
Opis API s primeri je na voljo na — široko znanem programu za testiranje HTTP API.
Demo aplikacija v oblaku Amazon
Aplikacijo sem naložil na Amazon, ker IBM še ni uspel nadgraditi mojega računa in omogočiti ustvarjanje virtualnih strežnikov. Na vrhu sem dodal domeno: . Malo bom obdržal strežnik vklopljen, potem ga izklopim, ker se zaračunava najem, kovanci citcoin pa na borzi še niso uvrščeni) V članek dodajam posnetke zaslona demo, da bo razumljena logika delovanja. Demo aplikacija lahko:
- Inicijalizirati blokovno verigo;
- Ustvarjati Račun (trenutno novega računa ni možno ustvariti, ker je v blokovni verigi doseženo maksimalno število računov, kot je zapisano v pametnem pogodbi);
- Pridobiti seznam računov;
- Prenesti kovance citcoin med Alicjo, Bobom in Aleksandrom;
- Pridobiti dogodke (trenutno dogodkov ni mogoče prikazati, zato je v vmesniku za enostavnost zapisano, da dogodki niso podprti);
- Zapisovati aktivnosti.
Najprej inicializiramo blokovno verigo:

Nato ustvarimo svoj račun, ne varčujemo s saldo:

Pridobimo seznam vseh razpoložljivih računov:

Izberemo pošiljatelja in prejemnika, pridobimo njihove salde. Če sta pošiljatelj in prejemnik ista oseba, bo njegov račun povečal saldo:

V dnevniku spremljamo izvrševanje transakcij:

S tem je demo program končan. Oglejte si našo transakcijo v blokovni verigi:

In skupni seznam transakcij:

Uspešno smo zaključili implementacijo PoC za ustvarjanje omrežja Citcoin. Kaj je še potrebno, da Citcoin postane popoln sistem za prenos kovancev? Skoraj nič:
- Pri ustvarjanju računa implementirati generacijo zasebnega/publikčnega ključa. Zasebni ključ mora biti shranjen pri uporabniku računa, javni v blokovni verigi.
- Uvesti prenos kovancev, pri čemer za identifikacijo uporabnika uporabimo ne ime, temveč javni ključ.
- Šifrirati transakcije, ki gredo od uporabnika na strežnik z njegovim zasebnim ključem.
Zaključek
Uresničili smo omrežje Citcoin s funkcijami: dodajanje računa, preverjanje stanja, polnjenje računa, prenos kovancev z enega računa na drugega. Kaj nas je torej stalo izgradnja PoC?
- Potrebno je preučiti blockchain na splošno in Hyperledger Fabric posebej;
- Naučiti se uporabljati IBMov ali Amazonove oblake;
- Naučiti se programskega jezika JS in kakšen spletni okvir;
- Če je treba nekatere podatke hraniti zunaj blockchaina, za integracijo z ločeno bazo, se naučiti, na primer, PostgreSQL;
- In zadnje, a ne manj pomembno - brez znanja Linuxa v sodobnem svetu nikamor!)
Seveda, to ni raketna znanost, a malo dela bo potrebno!
Izvorna koda na GitHubu
Izvorno kodo sem dal na . Kratek opis repozitorija:
Katalog „strežnik» - Node.js strežnik
Katalog „odjemalec» - Node.js odjemalec
Katalog „blockchain» (vrednosti parametrov in ključi so seveda neaktivni in navedeni le za primer):
- contract - izvorna koda pametnega pogodbe
- wallet - ključi uporabnika za uporabo Hyperledger Fabric API.
- *.cds - skompilirane različice pametnih pogodb
- *.json datoteke - primeri konfiguracijskih datotek za uporabo Hyperledger Fabric API
To je šele začetek!
Izvor: habr.com
