Смарт-контракты Docker с использованием REST API ноды¶
Подсказка
Техническое описание особенностей реализации контрактов приведено в разделе Смарт-контракты Docker.
Описание логики программы¶
В разделе рассматривается пример создания и запуска простого смарт-контракта. Контракт выполняет инкремент переданного на вход числа при каждой call-транзакции.
Листинг программы contract.py
на Python:
import json
import os
import requests
import sys
def find_param_value(params, name):
for param in params:
if param['key'] == name: return param['value']
return None
def print_success(results):
print(json.dumps(results, separators=(',', ':')))
def print_error(message):
print(message)
sys.exit(3)
def get_value(contract_id):
node = os.environ['NODE_API']
if not node:
print_error("Node REST API address is not defined")
token = os.environ["API_TOKEN"]
if not token:
print_error("Node API token is not defined")
headers = {'X-Contract-Api-Token': token}
url = '{0}/internal/contracts/{1}/sum'.format(node, contract_id)
r = requests.get(url, verify=False, timeout=2, headers=headers)
data = r.json()
return data['value']
if __name__ == '__main__':
command = os.environ['COMMAND']
if command == 'CALL':
contract_id = json.loads(os.environ['TX'])['contractId']
value = get_value(contract_id)
print_success([{
"key": "sum",
"type": "integer",
"value": value + 1}])
elif command == 'CREATE':
print_success([{
"key": "sum",
"type": "integer",
"value": 0}])
else:
print_error("Unknown command {0}".format(command))
Описание работы
Программа ожидает получить структуру данных в формате json с полем «params».
Считывает значение поля «а».
Возвращает результат в виде значения поля «{a}+1» в формате json.
Пример входящих параметров
"params":[
{
"key":"a",
"type":"integer",
"value":1
}
]
Установка смарт-контракта¶
Скачать и установить Docker for Developers для вашей операционной системы.
Подготовить образ контракта. В папке
stateful-increment-contract
создать следующие файлы:
contract.py
Dockerfile
run.sh
Листинг файла run.sh
#!/bin/sh
python contract.py
Листинг файла Dockerfile
FROM python:alpine3.8
ADD contract.py /
ADD run.sh /
RUN chmod +x run.sh
CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"
Установить образ в Docker registry. Выполнить в терминале следующие команды:
docker run -d -p 5000:5000 --name registry registry:2
cd contracts/stateful-increment-contract
docker build -t stateful-increment-contract .
docker image tag stateful-increment-contract localhost:5000/stateful-increment-contract
docker start registry
docker push localhost:5000/stateful-increment-contract
Для получения информации о контейнере выполнить в терминале следующую команду:
docker inspect 57c2c2d2643d
[
{
"Id": "sha256:57c2c2d2643da042ef8dd80010632ffdd11e3d2e3f85c20c31dce838073614dd",
"RepoTags": [
"wenode:latest"
],
"RepoDigests": [],
"Parent": "sha256:d91d2307057bf3bb5bd9d364f16cd3d7eda3b58edf2686e1944bcc7133f07913",
"Comment": "",
"Created": "2019-10-25T14:15:03.856072509Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
Идентификатор смарт-контракта Id
является значением поля imageHash
для использования в транзакциях с созданным смарт-контрактом.
Подписать транзакцию на создание смарт-контракта. В рассматриваемом примере транзакция подписывается ключом, сохраненным в keystore ноды.
Подсказка
Для создания ключевой пары и адреса участника используется утилита generators.jar. Порядок действий создания ключевой пары приведен в подразделе Генерирование ключевых пар. Правила формирования запросов к ноде приведены в разделе REST API ноды.
Тело запроса
{
"fee": 100000000,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2",
"password": "",
"params": [],
"type": 103,
"version": 1
}
Пример запроса
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d ' { \
"fee": 100000000, \
"image": "stateful-increment-contract:latest", \
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
"contractName": "stateful-increment-contract", \
"sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2", \
"password": "", \
"params": [], \
"type": 103, \
"version": 1 \
}' 'http://localhost:6862/transactions/sign'
Пример ответа
{
"type": 103,
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
"fee": 500000,
"timestamp": 1550591678479,
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
"version": 1,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"params": [],
"height": 1619
}
Отправить подписанную транзакцию в блокчейн. Ответ от метода sign необходимо передать на вход для метода broadcast.
Пример запроса
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
{
"type": 103, \
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M", \
"fee": 500000, \
"timestamp": 1550591678479, \
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ], \
"version": 1, \
"image": "stateful-increment-contract:latest", \
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
"contractName": "stateful-increment-contract", \
"params": [], \
"height": 1619 \
}
}' 'http://localhost:6862/transactions/broadcast'
По id транзакции убедиться, что транзакция с инициализацией контракта размещена в блокчейне.
Пример ответа
{
"type": 103,
"id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
"senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
"fee": 500000,
"timestamp": 1550591678479,
"proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
"version": 1,
"image": "stateful-increment-contract:latest",
"imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
"contractName": "stateful-increment-contract",
"params": [],
"height": 1619
}
Исполнение смарт-контракта¶
Подписать call-транзакцию на вызов (исполнение) смарт-контракта.
В поле contractId
указать идентификатор транзакции инициализации контракта.
Тело запроса
{
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"fee": 10,
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"password": "",
"type": 104,
"version": 1,
"params": [
{
"type": "integer",
"key": "a",
"value": 1
}
]
}
Пример запроса
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"fee": 10, \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"password": "", \
"type": 104, \
"version": 1, \
"params": [ \
{ \
"type": "integer", \
"key": "a", \
"value": 1 \
} \
] \
}' 'http://localhost:6862/transactions/sign'
Пример ответа
{
"type": 104,
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP",
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq",
"fee": 10,
"timestamp": 1549365736923,
"proofs": [
"2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v"
],
"version": 1,
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"params": [
{
"key": "a",
"type": "integer",
"value": 1
}
]
}
Отправить подписанную транзакцию в блокчейн. Ответ от метода sign необходимо передать на вход для метода broadcast.
Пример запроса
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"type": 104, \
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP", \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq", \
"fee": 10, \
"timestamp": 1549365736923, \
"proofs": [ \
"2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v" \
], \
"version": 1, \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"params": [ \
{ \
"key": "a", \
"type": "integer", \
"value": 1 \
} \
] \
}' 'http://localhost:6862/transactions/broadcast'
Получить результат выполнения смарт-контракта по его идентификатору.
Пример ответа
[
{
"key": "1+1",
"type": "integer",
"value": 2
}
]