Oczy się boją, a ręce się drapią!
W poprzednich artykułach omówiliśmy technologie, na których opierają się blockchainy () oraz przypadki użycia, które można z ich pomocą zrealizować (). Nadszedł czas, aby zabrać się do pracy! Do realizacji pilotów i PoC (Proof of Concept) preferuję używać chmur, ponieważ są dostępne z dowolnego miejsca na świecie i często nie trzeba tracić czasu na nudną instalację środowiska, ponieważ są dostępne prekonfigurowane ustawienia. Zróbmy coś prostego, na przykład sieć do transferu monet pomiędzy uczestnikami i nazwijmy ją skromnie Citcoin. W tym celu użyjemy chmury IBM oraz uniwersalnego blockchaina Hyperledger Fabric. Najpierw przyjrzyjmy się, dlaczego Hyperledger Fabric nazywa się uniwersalnym blockchainem?

Hyperledger Fabric — uniwersalny blockchain
Mówiąc ogólnie, uniwersalny system informacyjny to:
- Zestaw serwerów i rdzeń oprogramowania realizujący logikę biznesową;
- Interfejsy do interakcji z systemem;
- Środki do rejestracji, uwierzytelniania i autoryzacji urządzeń / ludzi;
- Baza danych, przechowująca dane operacyjne i archiwalne:

Oficjalną wersję, czym jest Hyperledger Fabric, można przeczytać na , a jeśli krótko, to Hyperledger Fabric to platforma open source, która umożliwia budowę zamkniętych blockchainów i realizację dowolnych inteligentnych kontraktów napisanych w językach programowania JS i Go. Przyjrzyjmy się szczegółowo architekturze Hyperledger Fabric i upewnijmy się, że jest to uniwersalny system, w którym istnieje jedynie specyfika przechowywania i zapisu danych. Specyfika polega na tym, że dane, jak we wszystkich blockchainach, są przechowywane w blokach, które są umieszczane w blockchainie tylko wtedy, gdy uczestnicy osiągnęli konsensus, a po zapisaniu danych nie można ich niezauważalnie poprawić ani usunąć.
Architektura Hyperledger Fabric
Na schemacie przedstawiona jest architektura Hyperledger Fabric:

Organizations — organizacje zawierają peer-y, przez co blockchain istnieje dzięki wsparciu organizacji. Różne organizacje mogą wchodzić w jeden channel.
Channel — struktura logiczna, łącząca peer-y w grupy, przez co definiowany jest blockchain. Hyperledger Fabric może jednocześnie obsługiwać wiele blockchainów z różną logiką biznesową.
Membership Services Provider (MSP) — to CA (Certificate Authority) do wydawania tożsamości i przydzielania ról. Aby utworzyć węzeł, należy współdziałać z MSP.
Węzły peer — sprawdzają transakcje, przechowują blockchain, wykonują inteligentne kontrakty i współdziałają z aplikacjami. Węzły peer mają tożsamość (certyfikat cyfrowy), który wydaje MSP. W przeciwieństwie do sieci Bitcoin lub Ethereum, gdzie wszystkie węzły są równe, w Hyperledger Fabric węzły pełnią różne role:
- Peer może być węzłem zatwierdzającym (EP) i wykonywać inteligentne kontrakty.
- Węzeł zatwierdzający (CP) — tylko przechowują dane w blockchainie i aktualizują „Stan świata”.
- Węzeł ankrujący (AP) — jeśli w blockchainie uczestniczy wiele organizacji, węzły ankrujące są używane do łączenia ich. Każda organizacja powinna mieć jeden lub więcej węzłów ankrujących. Dzięki AP każdy węzeł w organizacji może uzyskać informacje o wszystkich węzłach w innych organizacjach. Do synchronizacji informacji między AP używany jest .
- Węzeł lidera — jeśli organizacja ma kilka węzłów, to tylko węzeł lidera będzie odbierać bloki z usługi zamawiania i przekazywać je innym węzłom. Lider może być ustalony statycznie lub wybierany dynamicznie przez węzły w organizacji. Do synchronizacji informacji o liderach również używany jest protokół gossip.
Zasoby — podmioty o wartości, które są przechowywane w blockchainie. Dokładniej mówiąc — to dane key-value w formacie JSON. To właśnie te dane są zapisywane w blockchainie „Blockchain”. Posiadają historię, która jest przechowywana w blockchainie oraz aktualny stan, który jest przechowywany w bazie danych „Stan świata”. Struktury danych są wypełniane według potrzeb w zależności od zadań biznesowych. Nie ma żadnych obowiązkowych pól, jedynym zaleceniem jest — zasoby powinny mieć właściciela i reprezentować wartość.
Księga — składa się z blockchaina „Blockchain” oraz bazy danych „Stan świata”, w której przechowywany jest aktualny stan zasobów. Stan świata korzysta z LevelDB lub CouchDB.
Inteligentny kontrakt — za pomocą inteligentnych kontraktów realizowana jest logika biznesowa systemu. W Hyperledger Fabric inteligentne kontrakty nazywane są chaincode. Za pomocą chaincode definiuje się zasoby i transakcje związane z nimi. Mówiąc technicznym językiem, inteligentne kontrakty to moduły programowe realizowane w językach programowania JS lub Go.
Polityka zatwierdzania — dla każdego chaincode można ustalić polityki dotyczące tego, ile i od kogo należy oczekiwać potwierdzeń dla transakcji. Jeśli polityka nie jest określona, domyślnie stosuje się: „transakcję musi zatwierdzić każdy członek (member) każdej organizacji w kanale”. Przykłady polityk:
- Transakcję musi zatwierdzić każdy administrator organizacji;
- Wszyscy członkowie (member) lub klienci organizacji muszą ją zatwierdzić;
- Każdy peer organizacji musi to zatwierdzić.
Usługa porządkowania — pakuje transakcje w bloki i wysyła do peerów w channel. Gwarantuje dostarczenie wiadomości do wszystkich peerów w sieci. Do przemysłowych systemów wykorzystywana jest , do rozwoju i testowania .
CallFlow

- Aplikacja współdziała z Hyperledger Fabric, wykorzystując Go, Node.js lub Java SDK;
- Klient tworzy transakcję tx i wysyła ją do endorsujących peerów;
- Peer sprawdza podpis klienta, wykonuje transakcję i wysyła podpis zatwierdzenia z powrotem do klienta. Chaincode wykonuje się tylko na endorsujących peerach, a wynik jego wykonania jest rozsyłany do wszystkich peerów. Taki algorytm pracy nazywa się — konsensusem PBFT (Practical Byzantine Fault Tolerant). Różni się od tym, że wiadomość jest rozsyłana i oczekiwane jest potwierdzenie nie od wszystkich uczestników, a tylko od określonego zestawu;
- Po tym, jak klient otrzyma liczbę odpowiedzi odpowiadającą polityce zatwierdzenia, wysyła transakcję do usługi porządkowania;
- Usługa porządkowania tworzy blok i wysyła go do wszystkich peerów zatwierdzających. Usługa porządkowania zapewnia sekwencyjne zapisywanie bloków, co wyklucza tak zwany fork księgi głównej ();
- Peerzy otrzymują blok, ponownie sprawdzają politykę zatwierdzenia, zapisują blok w blockchainie i zmieniają stan w bazie danych „World state”.
To zatem prowadzi do podziału ról między węzłami. Zapewnia to skalowalność i bezpieczeństwo blockchaina:
- Smart kontrakty (chaincode) wykonują endorsujący peerzy. Zapewnia to prywatność smart kontraktów, ponieważ są one przechowywane nie przez wszystkich uczestników, a tylko na endorsujących peerach.
- Usługa porządkowania musi działać szybko. Zapewnia to fakt, że Usługa porządkowania tylko tworzy blok i wysyła go do ustalonego zestawu peerów liderów.
- Zatwierdzający peerzy tylko przechowują blockchain — może ich być wielu i nie wymagają dużej mocy obliczeniowej ani natychmiastowej pracy.
Szczegóły dotyczące architektonicznych rozwiązań Hyperledger Fabric oraz dlaczego działa tak, a nie inaczej, można zobaczyć tutaj: lub tutaj: .
Tak więc, Hyperledger Fabric to rzeczywiście uniwersalny system, za pomocą którego można:
- Realizować dowolną logikę biznesową, używając mechanizmu smart kontraktów;
- Zapisuj i odbieraj dane z bazy danych blockchain w formacie JSON;
- Zapewnij i zweryfikuj dostęp do API, korzystając z Certyfikatu Autoryzacji.
Teraz, gdy nieco zrozumieliśmy specyfikę Hyperledger Fabric, w końcu zróbmy coś użytecznego!
Rozwijamy blockchain
Sformułowanie zadania
Zadanie — zaimplementować sieć Citcoin z następującymi funkcjami: utworzyć konto, uzyskać saldo, doładować konto, przelać monety z jednego konta na drugie. Stwórzmy model obiektowy, który następnie zaimplementujemy w smart kontrakcie. Tak więc, będziemy mieli konta, które będą identyfikowane nazwami (name) i będą miały saldo (balance), a także listę kont. Konta i lista kont będą w terminologii Hyperledger Fabric aktywami. Odpowiednio, mają historię i bieżący stan. Postaram się to wizualnie nakreślić:

Górne figury to bieżący stan, który jest przechowywany w bazie „World state”. Poniżej figury pokazujące historię, która jest przechowywana w blockchainie. Bieżący stan aktywów zmienia się w wyniku transakcji. Aktywa zmieniają się tylko w całości, dlatego w wyniku wykonania transakcji powstaje nowy obiekt, a bieżąca wartość aktywa przechodzi do historii.
Chmura IBM
Tworzymy konto w . Aby skorzystać z platformy blockchain, należy ją zaktualizować do opłaty zgodnie z użytkowaniem. Proces ten może nie być szybki, ponieważ IBM prosi o dodatkowe informacje i sprawdza je ręcznie. Z pozytywnych rzeczy mogę powiedzieć, że IBM ma dobre materiały szkoleniowe, które pozwalają na uruchomienie Hyperledger Fabric w ich chmurze. Podobał mi się następujący cykl artykułów i przykładów:
Poniżej znajdują się zrzuty ekranu z platformy Blockchain IBM. To nie jest instrukcja tworzenia blockchaina, tylko demonstracja zakresu zadania. Tak więc, dla naszych celów tworzymy jedną organizację:

W niej tworzymy węzły: Orderer CA, Org1 CA, Orderer Peer:

Tworzymy użytkowników:

Tworzymy Channel i nazywamy go citcoin:

W istocie Channel to blockchain, dlatego zaczyna się od bloku zerowego (Genesis block):

Piszę 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;
Intuicyjnie powinno to być wszystko zrozumiałe:
- Istnieje kilka funkcji (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance), które będą wywoływane przez program demo za pomocą API Hyperledger Fabric.
- Funkcje SendFrom i RefillBalance generują zdarzenia (Event), które będzie otrzymywał program demo.
- Funkcja instantiate jest wywoływana raz podczas instancjonowania smart kontraktu. W rzeczywistości jest wywoływana nie raz, a za każdym razem, gdy zmienia się wersja smart kontraktu. Dlatego inicjalizacja listy pustą tablicą to zła idea, ponieważ teraz przy zmianie wersji smart kontraktu utracimy bieżącą listę. Ale nic, dopiero się uczę).
- Konta i lista kont (accounts) to struktury danych w formacie JSON. Do manipulacji danymi używany jest JS.
- Bieżącą wartość aktywów asset można uzyskać za pomocą wywołania funkcji getState, a zaktualizować za pomocą putState.
- Podczas tworzenia konta wywoływana jest funkcja AddAccount, w której następuje porównanie maksymalnej liczby kont w blockchainie (maxAccounts = 5). I tu jest błąd (zauważyliście?), który prowadzi do nieskończonego wzrostu liczby kont. Należy unikać takich błędów).
Następnie ładujemy smart kontrakt do Channel i instancjonujemy go:

Sprawdzamy transakcję instalacji Smart Contract:

Sprawdzamy szczegóły naszego Channel:

W rezultacie otrzymujemy następującą schemę sieci blockchain w chmurze IBM. Na schemacie znajduje się również program demo, uruchomiony w chmurze Amazon na wirtualnym serwerze (szczegółowo będzie o nim w następnym rozdziale):

Tworzenie GUI do wywoływania Hyperledger Fabric API
Hyperledger Fabric ma API, które może być używane do:
- Tworzenia channel;
- Podłączenia peera do channel;
- Instalacji i instancjonowania smart kontraktów w channel;
- Wywoływania transakcji;
- Żądania informacji w blockchainie.
Rozwój aplikacji
W naszym programie demo użyjemy API tylko do wywoływania transakcji i żądania informacji, ponieważ pozostałe kroki już wykonaliśmy, korzystając z platformy blockchain IBM. Tworzymy GUI, używając standardowego stosu technologii: Express.js + Vue.js + Node.js. O tym, jak rozpocząć tworzenie nowoczesnych aplikacji internetowych, można napisać osobny artykuł. Tutaj zostawię link do serii wykładów, które najbardziej mi się podobały: . W rezultacie powstała aplikacja kliencka z znanym interfejsem graficznym w stylu Material Design od Google. REST API między klientem a serwerem składa się z kilku wywołań:
- HyperledgerDemo/v1/init — zainicjować blockchain;
- HyperledgerDemo/v1/accounts/list — uzyskać listę wszystkich kont;
- HyperledgerDemo/v1/account?name=Bob&balance=100 — utworzyć konto Boba;
- HyperledgerDemo/v1/info?account=Bob — uzyskać informacje o koncie Boba;
- HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — przelać dwie monety od Boba do Alice;
- HyperledgerDemo/v1/disconnect — zamknąć połączenie z blockchainem.
Opis API z przykładami umieściłem na — powszechnie znanym programie do testowania API HTTP.
Aplikacja demo w chmurze Amazon
Aplikację umieściłem na Amazonie, ponieważ IBM wciąż nie zdołał zaktualizować mojego konta i zezwolić na tworzenie wirtualnych serwerów. Jako wisienkę na torcie dodałem domenę: . Utrzymam serwer włączony przez chwilę, potem go wyłączę, bo opłaty za wynajem naliczają się, a monety citcoin na giełdzie jeszcze nie są notowane) W artykule umieszczam zrzuty ekranu z demo, aby pokazać logikę działania. Aplikacja demo może:
- Inicjować blockchain;
- Tworzyć Konto (ale teraz nie można utworzyć nowego Konta, ponieważ w blockchainie osiągnięto maksymalną liczbę kont określoną w smart kontrakcie);
- Pobierać listę Kont;
- Przelać monety citcoin między Alice, Bobem i Alexem;
- Pobierać zdarzenia (ale teraz zdarzenia nie mogą być pokazywane, dlatego w interfejsie dla uproszczenia napisano, że zdarzenia nie są obsługiwane);
- Logować działania.
Najpierw inicjujemy blockchain:

Następnie zakładamy swoje konto, nie oszczędzając na saldzie:

Pobieramy listę wszystkich dostępnych kont:

Wybieramy nadawcę i odbiorcę, sprawdzamy ich salda. Jeśli nadawca i odbiorca są tacy sami, saldo zostanie doładowane:

W logu śledzimy realizację transakcji:

To wszystko w kontekście programu demo. Następnie można zobaczyć naszą transakcję w blockchainie:

I ogólną listę transakcji:

W ten sposób pomyślnie zakończyliśmy realizację PoC tworzenia sieci Citcoin. Co jeszcze należy zrobić, aby Citcoin stał się pełnoprawną siecią do transferu monet? Zaledwie kilka rzeczy:
- Na etapie tworzenia konta zrealizować generację klucza prywatnego / publicznego. Klucz prywatny powinien być przechowywany przez użytkownika konta, publiczny w blockchainie.
- Zrealizować transfer monet, w którym do identyfikacji użytkownika wykorzystywany jest nie jego imię, ale klucz publiczny.
- Szyfrować transakcje wychodzące od użytkownika do serwera jego kluczem prywatnym.
Podsumowanie
Zrealizowaliśmy sieć Citcoin z funkcjami: dodanie konta, uzyskanie salda, doładowanie swojego konta, przelanie monet z jednego konta na drugie. Więc co nas kosztowało zbudowanie PoC?
- Trzeba poznać blockchain w ogóle i Hyperledger Fabric w szczególności;
- Nauczyć się korzystać z chmur IBM lub Amazon;
- Nauka języka programowania JS i dowolnego frameworka webowego;
- Jeśli jakieś dane muszą być przechowywane nie w blockchainie, a w oddzielnej bazie, naucz się integrować, na przykład, z PostgreSQL;
- I ostatnia rzecz na liście, ale nie mniej ważna - bez znajomości Linuxa w nowoczesnym świecie ani rusz!)
Oczywiście, to nie jest rocket science, ale musisz się trochę napracować!
Kody źródłowe na GitHubie
Kody źródłowe wrzuciłem na . Krótkie opisanie repozytorium:
Katalog „server» — serwer Node.js
Katalog „client» — klient Node.js
Katalog „blockchain» (wartości parametrów i klucze, oczywiście, nie działają i są podane tylko jako przykład):
- contract - kod źródłowy smart kontraktu
- wallet - klucze użytkownika do korzystania z Hyperledger Fabric API.
- *.cds - skompilowane wersje smart kontraktów
- *.json pliki - przykłady plików konfiguracyjnych do użycia Hyperledger Fabric API
To dopiero początek!
Źródło: habr.com
