ProHoster > BLOG > administrare > Efectuați tranzacții publice și private pe blockchain-ul JPMorgan Quorum folosind Web3
Efectuați tranzacții publice și private pe blockchain-ul JPMorgan Quorum folosind Web3
Cvorum — блокчейн на базе Ethereum, разработанный JPMorgan и совсем недавно ставший первой платформой распределенного реестра, которую предлагает Microsoft Azure.
Quorum поддерживает приватные и публичные транзакции и имеет много коммерческих сценариев использования.
В данной статье мы разберем один из таких сценариев — развертывание сети распределенного реестра между супермаркетом и владельцем склада для обеспечения актуальной информации о температуре складского помещения.
Для иллюстрации используется сценарий мониторинга температуры в складских помещениях участников сети Quorum в рамках Internet of Things (IoT).
context
Группа складских компаний объединяется в консорциум для совместного хранения информации и автоматизации процессов на блокчейне. Для этого компании решили использовать Quorum. В данной статье мы покроем два сценария: публичные транзакции и приватные транзакции.
Транзакции создаются разными участниками для взаимодействия с консорциумом, которому они принадлежат. Каждая транзакция либо деплоит контракт, либо вызывает функцию в контракте для загрузки данных в сеть. Данные действия реплицируются на все ноды в сети.
Публичные транзакции доступны для просмотра всеми участниками консорциума. Приватные же транзакции добавляют слой конфиденциальности и доступны только тем участникам, у которых есть на это права.
Для обоих сценариев мы для наглядности используем один и тот же контракт.
Contract inteligent
Ниже приведен простой смарт-контракт, созданный для нашего сценария. В нем есть публичная переменная temperature, которую можно изменять методом set и получать методом get.
pragma solidity ^0.4.25;
contract TemperatureMonitor {
int8 public temperature;
function set(int8 temp) public {
temperature = temp;
}
function get() view public returns (int8) {
return temperature;
}
}
Для того, чтобы контракт работал с web3.js, его необходимо перевести в формат ABI и байткод. Использование функции formatContract, приведенной ниже, компилирует контракт при помощи solc-js.
Теперь, когда контракт готов, мы развернем сеть и задеплоим контракт.
Развертывание нод
Развертывание ноды может быть довольно трудоемким и этот процесс можно заменить использованием сервиса Chainstack.
Ниже приведен процесс развертывания сети Quorum с консенсусом Raft и тремя нодами.
Для начала заведем проект и назовем его Quorum Project:
Создадим сеть Quorum с консенсусом Raft на Google Cloud Platform:
К уже созданной по умолчанию ноде добавим еще две ноды:
Три запущенные ноды:
Страница деталей ноды показывает RPC endpoint, публичный ключ и т. д.
Сеть развернута. Теперь займемся деплоем смарт-контрактов и выполнением транзакций при помощи web3.js.
Публичные транзакции
context
Температура складского помещение имеет большое значение для снижения затрат, особенно для продуктов, предназначенных для хранения при минусовой температуре.
Давая компаниям возможность делиться значениями внешней температуры их географического положения в режиме реального времени и записывать в неизменяемый реестр, участники сети сокращают расходы и время.
Мы выполним три задачи, проиллюстрированные на диаграмме:
Задеплоим контракт через Nodul 1:
const contractAddress = await deployContract(raft1Node);
console.log(`Contract address after deployment: ${contractAddress}`);
Установим температуру через Nodul 2 на 3 градуса:
const status = await setTemperature(raft2Node, contractAddress, 3);
console.log(`Transaction status: ${status}`);
Nodul 3 получит информацию из смарт-контракта. Контракт вернет значение 3 градуса:
Далее рассмотрим как исполнить публичную транзакцию в сети Quorum с использованием web3.js.
Инициируем инстанс через RPC для трех нод:
const raft1Node = new Web3(
new Web3.providers.HttpProvider(process.env.RPC1), null, {
transactionConfirmationBlocks: 1,
},
);
const raft2Node = new Web3(
new Web3.providers.HttpProvider(process.env.RPC2), null, {
transactionConfirmationBlocks: 1,
},
);
const raft3Node = new Web3(
new Web3.providers.HttpProvider(process.env.RPC3), null, {
transactionConfirmationBlocks: 1,
},
);
Задеплоим смарт-контракт:
// returns the default account from the Web3 instance initiated previously
function getAddress(web3) {
return web3.eth.getAccounts().then(accounts => accounts[0]);
}
// Deploys the contract using contract's interface and node's default address
async function deployContract(web3) {
const address = await getAddress(web3);
// initiate contract with contract's interface
const contract = new web3.eth.Contract(
temperatureMonitor.interface
);
return contract.deploy({
// deploy contract with contract's bytecode
data: temperatureMonitor.bytecode,
})
.send({
from: address,
gas: '0x2CD29C0',
})
.on('error', console.error)
.then((newContractInstance) => {
// returns deployed contract address
return newContractInstance.options.address;
});
}
web3.js предоставляет два метода для взаимодействия с контрактом: call и send.
Обновим температуру контракта выполнением set используя метод web3 send.
// get contract deployed previously
async function getContract(web3, contractAddress) {
const address = await getAddress(web3);
return web3.eth.Contract(
temperatureMonitor.interface,
contractAddress, {
defaultAccount: address,
}
);
}
// calls contract set method to update contract's temperature
async function setTemperature(web3, contractAddress, temp) {
const myContract = await getContract(web3, contractAddress);
return myContract.methods.set(temp).send({}).then((receipt) => {
return receipt.status;
});
}
Далее используем метод web3 call для получения температуры контракта. Обратите внимание, что метод call выполняется на локальной ноде и на блокчейне транзакция создана не будет.
// calls contract get method to retrieve contract's temperature
async function getTemperature(web3, contractAddress) {
const myContract = await getContract(web3, contractAddress);
return myContract.methods.get().call().then(result => result);
}
Teper mojno zapustit public.js для получения следующего результата:
// Execute public script
node public.js
Contract address after deployment: 0xf46141Ac7D6D6E986eFb2321756b5d1e8a25008F
Transaction status: true
Retrieved contract Temperature 3
Далее мы можем посмотреть записи в эксплорере Quorum в панели Chainstack, как показано ниже.
Все три ноды провзаимодействовали и транзакции обновились:
Первая транзакция задеплоила контракт.
Вторая транзакция установила температуру контракта в 3 градуса.
Получение температуры происходит через локальную ноду, поэтому транзакция не создана.
Приватные транзакции
context
Частым требованием организаций является защита данных. В качестве примера рассмотрим сценарий, в котором Cупермаркет арендует складское помещение для хранения морепродуктов у отдельного Вендора:
Furnizor при помощи IoT-датчиков считывает температурные значения каждые 30 секунд и передает их Cупермаркету;
данные значения должны быть доступны только Вендору и Супермаркету, объединенным в сеть консорциум.
Мы выполним четыре задачи, проиллюстрированные на диаграмме выше.
Используем те же три ноды из предыдущего сценария для демонстрации приватных транзакций:
Supermarket деплоит смарт-контракт, который является приватным для Супермаркета и Вендора.
A treia latură не имеет права доступа к смарт-контракту.
Мы вызовем методы get и set în numele Супермаркета и Вендора для демонстрации приватной транзакции Quorum.
Задеплоим приватный контракт для участников Supermarket и Furnizor через участника Supermarket:
Установим температуру от Третьей стороны (внешняя нода) и получим значение температуры:
// Attempts to set Contract temperature to 10, this will not mutate contract's temperature
await setTemperature(
raft3Node,
contractAddress,
process.env.PK1,
10,
);
// This returns null
const temp = await getTemperature(raft3Node, contractAddress);
console.log(`[Node3] temp retrieved after updating contract from external nodes: ${temp}`);
Установим температуру от Вендора (внутренняя нода) и получим значение температуры:
Температура в данном сценарии должна вернуть из смарт-контракта значение 12. Обратите внимание, что Furnizor здесь имеет разрешенный доступ к смарт-контракту.
// Updated Contract temperature to 12 degrees
await setTemperature(
raft2Node,
contractAddress,
process.env.PK1,
12,
);
// This returns 12
const temp2 = await getTemperature(raft2Node, contractAddress);
console.log(`[Node2] temp retrieved after updating contract from internal nodes: ${temp2}`);
Получим температуру от Третьей стороны (внешняя нода):
На шаге 3 температура была установлена со значением 12, но A treia latură не имеет доступа к смарт-контракту. Поэтому возврат значения должен быть null.
// This returns null
const temp3 = await getTemperature(raft3Node, contractAddress);
console.log(`[Node3] temp retrieved from external nodes after update ${temp}`);
Далее более детально рассмотрим выполнение приватных транзакций в сети Quorum с web3.js. Поскольку большая часть кода совпадает с публичными транзакциями, выделим только те части, которые отличаются для приватных транзакций.
Обратите внимание, что контракт, загруженный в сеть, является неизменяемым, поэтому доступ с разрешением (permissioned) должен быть выдан соответствующим нодам путем включения публичного контракта на момент деплоя контракта, а не после.
async function deployContract(web3, publicKey) {
const address = await getAddress(web3);
const contract = new web3.eth.Contract(
temperatureMonitor.interface,
);
return contract.deploy({
data: temperatureMonitor.bytecode,
})
.send({
from: address,
gas: ‘0x2CD29C0’,
// Grant Permission to Contract by including nodes public keys
privateFor: [publicKey],
})
.then((contract) => {
return contract.options.address;
});
}
Подобным же образом выполняются приватные транзакции — путем включения публичного ключа участников на момент выполнения.
async function setTemperature(web3, contractAddress, publicKey, temp) {
const address = await getAddress(web3);
const myContract = await getContract(web3, contractAddress);
return myContract.methods.set(temp).send({
from: address,
// Grant Permission by including nodes public keys
privateFor: [publicKey],
}).then((receipt) => {
return receipt.status;
});
}
Теперь мы можем запустить private.js со следующими результатами:
node private.js
Contract address after deployment: 0x85dBF88B4dfa47e73608b33454E4e3BA2812B21D
[Node3] temp retrieved after updating contract from external nodes: null
[Node2] temp retrieved after updating contract from internal nodes: 12
[Node3] temp retrieved from external nodes after update null
Эксплорер Quorum в Chainstack покажет следующее:
деплой контракта от участника Supermarket;
execuție SetTemperature din Третьей стороны;
execuție SetTemperature от участника Furnizor.
Как видите, обе транзакции выполнены, но только транзакция от участника Furnizor обновила температуру в контракте. Таким образом, приватные транзакции обеспечивают неизменяемость, но при этом и не выдают данные третьей стороне.
Concluzie
Мы рассмотрели коммерческий сценарий использования Quorum для обеспечения актуальной информации о температуре в складском помещении путем развертывания сети между двумя сторонами — супермаркетом и владельцем склада.
Мы показали как актуальная информация о температуре может поддерживаться как через публичные, так и через приватные транзакции.
Сценариев применения может быть очень много и, как видите, это совсем не сложно.