Очите се страхуват, а ръцете се сърбят!
В предишните статии разгледахме технологиите, на които се основават блокчейните () и казусите, които могат да бъдат реализирани с тяхна помощ (). Време е да поработим с ръце! За реализиране на пилоти и PoC (Proof of Concept) предпочитам да използвам облаците, тъй като те са достъпни от всяка точка на света и често не е нужно да губя време в скучна инсталация на среда, тъй като има предварително инсталирани конфигурации. И така, нека направим нещо просто, например, мрежа за прехвърляне на монети между участниците и нека я наречем скромно Сitcoin. За целта ще използваме облака на IBM и универсалния блокчейн Hyperledger Fabric. Първо, нека разгледаме защо Hyperledger Fabric се нарича универсален блокчейн?

Hyperledger Fabric — универсален блокчейн
Ако говорим общо, универсалната информационна система е:
- Набор от сървъри и софтуерно ядро, изпълняващо бизнес логиката;
- Интерфейси за взаимодействие с системата;
- Средства за регистрация, удостоверяване и авторизация на устройства/хора;
- База данни, която съхранява оперативни и архивни данни:

Официалната версия за това какво представлява Hyperledger Fabric може да бъде прочетена на , а ако да обобщим, Hyperledger Fabric — това е платформа с отворен код, позволяваща изграждането на затворени блокчейни и извършването на произволни смарт-контракти, написани на програмиращи езици JS и Go. Нека разгледаме подробно архитектурата на Hyperledger Fabric и да се уверим, че това е универсална система, в която само спецификата е свързана със съхранението и записването на данни. Спецификата се състои в това, че данните, както и в повечето блокчейни, се съхраняват в блокове, които се добавят в блокчейна само ако участниците постигнат съгласие, и след записа данните не могат да бъдат незабелязано променени или изтрити.
Архитектура на Hyperledger Fabric
На схемата представена архитектура на Hyperledger Fabric:

Organizations — организациите съдържат peer-ове, т.е. блокчейнът съществува благодарение на подкрепата на организациите. Различни организации могат да входят в един channel.
Channel — логическа структура, обединяваща peer-ове в групи, т.е. определя се блокчейн. Hyperledger Fabric може едновременно да обработва няколко блокчейна с различна бизнес логика.
Membership Services Provider (MSP) — това е CA (Certificate Authority) за издаване на идентичности и присвояване на роли. За създаването на нода е необходимо да се взаимодействува с 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 протокол.
Активи — субекти, които имат стойност и се съхраняват в блокчейна. По-конкретно — това са key-value данни в формат JSON. Именно тези данни се записват в блокчейна „Blockchain“. Те имат история, която се съхранява в блокчейна, и текущо състояние, което се съхранява в базата данни „World state“. Данните се попълват произволно в зависимост от бизнес задачите. Няма задължителни полета, единствената препоръка е — активите да имат собственик и да представляват стойност.
Ledger — се състои от блокчейна „Blockchain“ и базата данни „World state“, в която се съхранява текущото състояние на активите. World state използва LevelDB или CouchDB.
Смарт контракт — с помощта на смарт-контрактите се реализира бизнес логиката на системата. В Hyperledger Fabric смарт-контрактите се наричат chaincode. С помощта на chaincode се задават активите и транзакциите над тях. Технически казано, смарт-контрактите са програмни модули, реализирани на програмни езици JS или Go.
Политика за одобрение — за всеки chaincode може да се зададат политики колко и от кого е необходимо да се очакват одобрения за транзакция. Ако политиката не е зададена, по подразбиране се използва: „транзакцията трябва да бъде потвърдена от всеки член (member) на която и да е организация в channel“. Примери за политики:
- Транзакцията трябва да бъде потвърдена от всеки администратор на организацията;
- Всеки член (member) или клиент на организацията трябва да потвърди;
- Всеки peer на организацията трябва да потвърди.
Ordering service — опакова транзакции в блокове и ги изпраща на peer-ите в channel. Гарантира доставката на съобщения до всички peer-ове в мрежата. За индустриални системи се използва , за разработка и тестване .
CallFlow

- Приложението взаимодейства с Hyperledger Fabric, използвайки Go, Node.js или Java SDK;
- Клиентът създава транзакция tx и я изпраща на endorsing peer-ите;
- Peer-ът проверява подписа на клиента, извършва транзакцията и изпраща endorsement signature обратно на клиента. Chaincode се изпълняват само на endorsing peer, а резултатът от изпълнението му се разпраща на всички peer-ове. Тази работа се нарича — PBFT (Practical Byzantine Fault Tolerant) консенсус. Различава се от по това, че съобщението се разпраща и се очаква потвърждение не от всички участници, а само от определен набор;
- След като клиентът получи брой отговори, съответстващ на endorsement policy, той изпраща транзакцията на Ordering service;
- Ordering service формира блок и го изпраща на всички committing peer-ове. Ordering service осигурява последователно записване на блоковете, което изключва така наречения ledger fork ();
- Peer-ите получават блока, отново проверяват endorsement policy, записват блока в блокчейна и променят състоянието в „World state“ DB.
Т.е. получава се разделение на роли между нодовете. Това осигурява масштабируемост и безопасност на блокчейна:
- Смарт-контрактите (chaincode) се изпълняват от endorsing peer-ите. Това осигурява конфиденциалност на смарт-контрактите, тъй като те не се съхраняват от всички участници, а само на endorsing peer-ите.
- Ordering трябва да работи бързо. Това се осигурява, тъй като Ordering само формира блока и го изпраща на фиксиран набор от leader peer-ове.
- Committing peers само съхраняват блокчейна — те могат да бъдат много и не изискват голяма мощност и незабавна работа.
Подробности за архитектурните решения на Hyperledger Fabric и защо работи по този начин, можете да видите тук: или тук: .
Така че, Hyperledger Fabric е наистина универсална система, с помощта на която можете да:
- Реализирате произволна бизнес логика, използвайки механизма на смарт-контрактите;
- Записване и получаване на данни от блокчейн база данни в формат JSON;
- Предоставяне и проверка на достъп до API, използвайки Оставителна Агенция.
Сега, след като малко се запознахме със спецификата на Hyperledger Fabric, нека най-после направим нещо полезно!
Разгръщане на блокчейн
Формулиране на задачата
Задачата е да реализираме мрежа Citcoin с следните функции: създаване на акаунт, получаване на баланс, попълване на сметката, преместване на монети от един акаунт на друг. Ще нарисуваме обектна модел, който след това ще реализираме в смарт-контракт. И така, ще имаме акаунти, които се идентифицират с имената (name) и съдържат баланс (balance), и списък с акаунти. Акаунтите и списъкът с акаунти са, в термини на Hyperledger Fabric, активи. Съответно, те имат история и текущо състояние. Ще опитам да го нарисувам визуално:

Горните фигури представляват текущото състояние, което се съхранява в базата "World state". Под тях фигури, показващи историята, която се съхранява в блокчейна. Текущото състояние на активите се променя чрез транзакции. Активът се променя само изцяло, затова в резултат на изпълнението на транзакцията се създава нов обект, а текущата стойност на актива отива в историята.
IBM Cloud
Създаване на акаунт в . За да използвате блокчейн платформата, трябва да я обновите до Pay-As-You-Go. Този процес може да не бъде бърз, тъй като IBM изисква допълнителна информация и я проверява ръчно. От позитивна страна мога да кажа, че IBM разполага с добри учебни материали, които позволяват разгръщане на Hyperledger Fabric в тяхната облачна среда. Хареса ми следният цикъл статии и примери:
По-долу са представени скрийншотове от облачната платформа на IBM. Това не е инструкция за създаване на блокчейн, а просто демонстрация на обема на задачата. И така, за нашите цели създаваме една Организация:

В нея създаваме нодове: Orderer CA, Org1 CA, Orderer Peer:

Създаваме потребители:

Създаваме Channel и го наричаме citcoin:

По същество Channel е блокчейн, затова той започва с нулев блок (Genesis block):

Пишем 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;
Интуитивно тук всичко трябва да е ясно:
- Има няколко функции (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance), които демо програмата ще извиква чрез Hyperledger Fabric API.
- Функциите SendFrom и RefillBalance генерират събития (Event), които демо програмата ще получава.
- Функцията instantiate се извиква веднъж при инстанцирането на смарт договора. Всъщност, тя се извиква не веднъж, а всеки път, когато се променя версията на смарт договора. Затова инициализацията на списъка с празен масив е лоша идея, тъй като сега при смяна на версията на смарт договора ще загубим текущия списък. Но нищо, все пак само уча).
- Акаунтите и списъкът с акаунти (accounts) са JSON структури от данни. За манипулиране на данните се използва JS.
- Текущото значение на asset-а може да се получи чрез извикване на функцията getState, а обновяването става чрез putState.
- При създаването на акаунт се извиква функцията AddAccount, в която се извършва сравнение с максималния брой акаунти в блокчейна (maxAccounts = 5). Тук има проблем (забелязахте ли?), който води до безкраен растеж на броя на акаунтите. Такива грешки трябва да се избягват)
След това зареждаме смарт договора в Channel и го инстанцираме:

Гледаме транзакцията за инсталиране на Smart Contract:

Гледаме детайлите за нашия Channel:

В резултат получаваме следната схема на блокчейн мрежа в облака IBM. Също така на схемата присъства демонстрационна програма, стартирана в облака Amazon на виртуален сървър (подробно за нея ще бъде в следващия раздел):

Създаване на GUI за извиквания на Hyperledger Fabric API
Hyperledger Fabric има API, което може да се използва за:
- Създаване на channel;
- Свързване на peer с channel;
- Инсталиране и инстанциране на смарт договори в channel;
- Извикване на транзакции;
- Запитване за информация в блокчейна.
Разработване на приложение
В нашата демонстрационна програма ще използваме API само за извикване на транзакции и запитване за информация, тъй като останалите стъпки вече сме направили, използвайки блокчейн платформата IBM. Пишем GUI, използвайки стандартен технологичен стек: Express.js + Vue.js + Node.js. За това как да започнете да създавате съвременни уеб приложения може да се напише отделна статия. Тук ще оставя линк към серия лекции, която ми хареса най-много: . В резултат се получи клиент-сървърно приложение с познат графичен интерфейс в стил Material Design от Google. REST API между клиента и сървъра се състои от няколко извиквания:
- HyperledgerDemo/v1/init — инициализиране на блокчейна;
- HyperledgerDemo/v1/accounts/list — получаване на списък с всички акаунти;
- HyperledgerDemo/v1/account?name=Bob&balance=100 — създаване на акаунт Bob;
- HyperledgerDemo/v1/info?account=Bob — получаване на информация за акаунта Bob;
- HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — превеждане на две монети от Bob на Alice;
- HyperledgerDemo/v1/disconnect — затваряне на връзката с блокчейна.
Описание на API с примери можете да намерите на — широко известна програма за тестване на HTTP API.
Демо приложение в облака на Amazon
Заредих приложението в Amazon, понеже IBM все още не успя да обнови моята сметка и да разреши създаването на виртуални сървъри. За завършек добавих домейн: . Ще поддържам сървъра включен за малко, след което ще го изключа, тъй като наема натрупва разходи, а монетите citcoin на борсата все още не се котират) В статията ще поставя екранни снимки на демото, за да е ясна логиката на работа. Демонстрационното приложение може:
- Да инициализира блокчейна;
- Да създаде акаунт (но в момента не може да се създаде нов акаунт, тъй като в блокчейна е достигнато максималното число на акаунти, указано в смарт договора);
- Да получи списък с акаунти;
- Да прехвърля монети citcoin между Alice, Bob и Alex;
- Да получава събития (но в момента събитията не могат да се покажат, затова в интерфейса за опростеност е написано, че събитията не се поддържат);
- Да логва действия.
Първо инициализираме блокчейна:

След това създаваме свой акаунт, без да пестим с баланса:

Получаваме списък на всички налични акаунти:

Избираме подател и получател, получаваме техните баланси. Ако подателят и получателят са един и същ, ще се попълни неговата сметка:

В логовете следим изпълнението на транзакциите:

С демо приложението на това е всичко. След това можем да видим нашата транзакция в блокчейна:

И общ списък с транзакции:

С успешно завършване на PoC по създаването на мрежата Citcoin, какво още трябва да направим, за да стане Citcoin пълноценна мрежа за прехвърляне на монети? Съвсем малко:
- На етапа на създаване на акаунта да реализираме генерирането на частен / публичен ключ. Частният ключ трябва да се съхранява от потребителя на акаунта, а публичният в блокчейна.
- Да се направи прехвърляне на монети, при което за идентификация на потребителя се използва не името, а публичният ключ.
- Да се криптират транзакциите, които отиват от потребителя към сървъра с неговия частен ключ.
Заключение
Ние реализирахме мрежата Citcoin с функции: добавяне на акаунт, получаване на баланс, попълване на сметка, прехвърляне на монети от една сметка на друга. Така че какво ни струва разработването на PoC?
- Трябва да проучим блокчейна изобщо и Hyperledger Fabric в частност;
- Да се научим да ползваме облаците на IBM или Amazon;
- Да овладеем езика за програмиране JS и някакъв уеб фреймуърк;
- Ако трябва да съхранявате данни извън блокчейна, а в отделна база, научете се да интегрирате, например, с PostgreSQL;
- И на последно място, но не по важност — без познания по Linux в съвременния свят не можете да мърдате!)
Разбира се, не е нещо сложно, но ще трябва да поработите!
Изходните файлове на GitHub
Изходните файлове са поставени на . Кратко описание на хранилището:
Каталог «сървер» — Node.js сървър
Каталог «client» — Node.js клиент
Каталог «blockchain» (стойностите на параметрите и ключовете, разбира се, неработещи и посочени само за пример):
- contract — изходен код на смарт договора
- wallet — ключовете на потребителя за използване на Hyperledger Fabric API.
- *.cds — компилирани версии на смарт договорите
- *.json файлове — примери на конфигурационни файлове за използване на Hyperledger Fabric API
Вече сме на самото начало!
Източник: habr.com
