Как устроена платформа

Смарт-аккаунт

Каждая транзакция на блокчейн-платформе Waves Enterprise создана от имени какого-либо аккаунта. Благодаря использованию открытого и закрытого ключа можно удостовериться, что выпущенная с аккаунта транзакция в действительности была отправлена с этого аккаунта.

Но пары ключей может оказаться недостаточно для обеспечения безопасности транзакций. Например, утечка мнемонической фразы от аккаунта позволит злоумышленнику отправлять в блокчейн транзакции от имени аккаунта.

Чтобы повысить безопасность транзакций на блокчейн-платформе Waves Enterprise реализована технология смарт-аккаунта (Smart Account). Смарт-аккаунт – это аккаунт, на котором установлен скрипт, который проверяет все отправляемые аккаунтом транзакции на соответствие указанным в скрипте условиям. Этот скрипт позволяет проводить валидации исходящих транзакций, например, на множественную подпись (multisig). Ниже приведены некоторые примеры параметров, которые скрипт может использовать для проверки транзакций:

  • Тип транзакции — можно разрешить отправку транзакций только заданного в скрипте типа;

  • Подтверждение или подпись транзакции — можно установить правило, согласно которому массив подтверждений proofs в теле транзакции должен содержать определенную подпись транзакции, несколько определенных подписей или другие данные;

  • Текущая высота блокчейна — владелец аккаунта может установить правило, согласно которому транзакции могут отправляться с его адреса только в том случае, если высота блокчейна превышает указанное в скрипте число N;

  • Произвольные данные, существующие в блокчейне — например, данные оракулов.

Также с помощью скрипта можно отменить все проверки, установив правило, согласно которому все транзакции, отправляемые с адреса, должны считаться валидными.

Со смарт-аккаунта могут быть отправлены только те транзакции, которые прошли валидацию скриптом.

Создание скрипта аккаунта

Владелец аккаунта создаёт скрипт аккаунта на языке RIDE.

Структура скрипта аккаунта

Скрипт аккаунта состоит из директивы и выражения.

Директива

В начале скрипта размещается директива. Например:

{-# STDLIB_VERSION 2 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ACCOUNT #-}

Приведенная выше директива состоит из трёх аннотаций и сообщает компилятору следующую информацию:

  • в скрипте используется версия 2 библиотеки стандартных функций,

  • типом содержимого данного скрипта является Expression,

  • создаваемый скрипт будет скриптом аккаунта.

Выражение

Выражение проверяет отправляемые аккаунтом транзакции на соответствие заданным условиям. Если условия не соблюдаются, транзакция не будет отправлена. Возможны следующие результаты выполнения выражения:

  • true – транзакция разрешена,

  • false – транзакция запрещена,

  • ошибка.

Установка скрипта на аккаунт

Установить скрипт на аккаунт можно только с помощью транзакции 13. SetScript Transaction. При этом у аккаунта, отправляющего эту транзакцию, должна быть только роль contract_developer, либо не должно быть ролей вообще.

К аккаунту можно прикрепить только один скрипт.

Открепить скрипт от смарт-аккаунта или заместить старый скрипт новым можно, только если старый скрипт не запрещает это. Для открепления или замены скрипта требуется отправить новую транзакцию установки скрипта SetScript Transaction.

Пример создания и применения скрипта аккаунта

В этом разделе приведен пример создания и развертывания скрипта вручную без использования клиентских библиотек и библиотек API.

В этом примере будет создан и развернут простой скрипт аккаунта, который проверяет наличие множественной подписи (две из двух) транзакции.

Предварительные условия

При создании этого скрипта аккаунта должны быть выполнены следующие условия:

  1. У вас есть нода в блокчейн сети Waves Enterprise.

  2. У вас есть три сгенерированных адреса в блокчейн сети Waves Enterprise:

    • 3MxjWXEUcVCeiaEUqNcorB5HxSpLsgJCGxE – аккаунт alice,

    • 3MqGVvfgqdqqU6P9mTAsLSxyRoRjrHF18Mf – аккаунт bob,

    • 3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X – общий аккаунт.

Создание скрипта

В этом примере создается следующий скрипт:

  • В первых двух строках скрипта определяются 2 открытых ключа, закодированных в base58, для адресов alice и bob.

  • После этого пользователи собирают 2 открытых ключа в полях proofs[0] и proofs[1].

    Баланс аккаунта пополняется членами команды, после чего, когда 2 из 2 членов команды решают потратить деньги, они предоставляют свои подписи в одной транзакции.

  • Скрипт смарт-аккаунта, используя функцию sigVerify, проверяет эти подписи в proofs, и если две из двух подписей действительны, то и транзакция считается действительной; в противном случае транзакция не проходит в блокчейн.

В скрипте нет директивы, поэтому будут выбраны автоматические значения.

let alicePubKey  = base58'Ey6Z9XkWsvG8JZwyxhkTjydRcGp1wg6rbC3AYcxq7Efr'
let bobPubKey    = base58'5PvhyouzHn2Pcev56oBvwpnsGK5fEu1dA8fM2nJQM4HR'

let aliceSigned  = if(sigVerify(tx.bodyBytes, tx.proofs[0], alicePubKey)) then 1 else 0
let bobSigned    = if(sigVerify(tx.bodyBytes, tx.proofs[1], bobPubKey  )) then 1 else 0
aliceSigned + bobSigned == 2

Конвертация скрипта в формат Base64

Используйте метод /utils/script/compile для компиляции скрипта и конвертации скрипта в формат Base64.

Для этого вы можете использовать Swagger:

../_images/account_script1.png

Или вы можете использовать curl, чтобы скомпилировать скрипт и конвертировать его в формат Base64:

curl -X POST "http://localhost:6862/utils/script/compile" -H  "accept: application/json" -H  "Content-Type: application/json" -d "let alicePubKey  = base58'Ey6Z9XkWsvG8JZwyxhkTjydRcGp1wg6rbC3AYcxq7Efr'let bobPubKey    = base58'5PvhyouzHn2Pcev56oBvwpnsGK5fEu1dA8fM2nJQM4HR'let aliceSigned  = if(sigVerify(tx.bodyBytes, tx.proofs[0], alicePubKey)) then 1 else 0let bobSigned    = if(sigVerify(tx.bodyBytes, tx.proofs[1], bobPubKey  )) then 1 else 0aliceSigned + bobSigned == 2"

Прикрепление скрипта к аккаунту

Чтобы прикрепить сконвертированный в формат Base64 скрипт к аккаунту, выполните следующие шаги:

  1. Подготовьте JSON транзакции 13. SetScript Transaction для подписания. В качестве отправителя укажите общий аккаунт; в поле script задайте скрипт аккаунта:

    {
      "type": 13,
      "version": 1,
      "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X",
      "fee": 100000,
      "script": "<script>"
    }
    
  2. Подпишите транзакцию с помощью метода /transactions/sign:

    $ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' \
    --header 'X-API-Key: <it is a secret>' \
    -d '{ "type": 13, "version": 1, "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X", "fee": 100000, \
    "script": "<script>" }' 'https://example.org/transactions/sign'
    

    Метод вернёт JSON, готовый к публикации:

    {
      "type": 13,
      "id": "8w7yauNiENsJP8oDUpVEfiAzyEzMKoXbJEqS26Ht99mg",
      "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X",
      "senderPublicKey": "66xdGznqt2AVLMZRHme9vFPC6cvN4yV95wRWPfTus3Qe",
      "fee": 100000,
      "timestamp": 1525797758819,
      "proofs": [
        "4Ro4e4UrsVkaFbHtu96qZwHAdf8N4TtpjSGik9kRusmmYKCxicdsEqcgQrYden36nurqhY9EBkTKwD499kAi5rxe"
      ],
      "version": 1,
      "script": "<script>"
    }
    
  3. Опубликуйте полученный JSON в блокчейн с помощью метода POST /transactions/broadcast:

    $ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' \
    --header 'X-API-Key: <it is a secret>' \
    -d '{ "type": 13, "id": "8w7yauNiENsJP8oDUpVEfiAzyEzMKoXbJEqS26Ht99mg", "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X", \
    "senderPublicKey": "66xdGznqt2AVLMZRHme9vFPC6cvN4yV95wRWPfTus3Qe", "fee": 100000, "timestamp": 1525797758819, \
    "proofs": [ "4Ro4e4UrsVkaFbHtu96qZwHAdf8N4TtpjSGik9kRusmmYKCxicdsEqcgQrYden36nurqhY9EBkTKwD499kAi5rxe" ], \
    "version": 1, "script": "<script>" }' \
    'https://example.org/transactions/broadcast'
    
  4. Проверьте, применился ли скрипт:

    $ curl http://example.org/addresses/scriptInfo/3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X
    {
    "address" : "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X",
    "script" : "<script>",
    "scriptText" : "<scriptText>",
    "complexity" : 27,
    "extraFee" : 400000
    }
    

где <scriptText> – строковое представление скомпилированного <script> (дерева выражений).

Теперь при отправке переводов с общего аккаунта с помощью транзакции 4. Transfer Transaction на другой аккаунт, скрипт будет проверять наличие обеих подписей (аккаунта alice и аккаунта bob) в поле proofs. Если подписи хотя бы одного из аккаунтов не будет, скрипт вернёт ошибку «State check failed. Reason: TransactionNotAllowedByScript» и транзакция перевода не будет опубликована в сети.

Смотрите также