Technical description of the platform

Smart Account

Every transaction on the Waves Enterprise blockchain platform is created on behalf of an account. Through the use of public and private keys, you can verify that a transaction issued from an account was actually sent from that account.

But the key pair may not be enough to secure transactions. For example, the account mnemonic phrase leakage would allow an attacker to send transactions to the blockchain on behalf of the account.

To increase the security of transactions, the Waves Enterprise blockchain platform implements Smart Account technology. A Smart Account is an account with a script that checks all the transactions sent by the account for compliance with the conditions specified in the script. This script allows outgoing transactions to be validated, for example, for multisig. Below are some examples of parameters that the script can use to validate transactions:

  • Transaction Type — only the transactions of the type specified in the script can be sent;

  • Transaction Confirmation or Signature — you can set a rule that the proofs confirmation array in the transaction body must contain a specific transaction signature, multiple specific signatures, or other data;

  • Current blockchain height — the account owner may set the rule that transactions can only be sent from his address if the blockchain height exceeds the number N specified in the script;

  • Arbitrary data that exists on the blockchain — for example, oracles data.

You can also use the script to override all validations by setting the rule that all the transactions sent from the address must be considered valid.

Only the transactions that have been validated by the script can be sent from the smart account.

Creating an account script

The account owner creates an account script in the RIDE language.

Account script structure

An account script consists of a directive and an expression.

Directive

A directive is placed at the beginning of the script. For example:

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

The above directive consists of three annotations and provides the compiler with the following information:

  • the script uses version 2 of the library of standard functions,

  • the type of script content is Expression,

  • the script being created will be an account script.

Expression

The expression checks the transactions sent by the account for compliance with the specified conditions. If the conditions are not met, the transaction will not be sent. The following results of the expression execution are possible:

  • true - the transaction is allowed,

  • false - the transaction is prohibited,

  • error.

Attaching an account script to an account

You can only install a script on an account using the 13. SetScript Transaction. The account sending this transaction must have only the contract_developer role, or no role at all.

You can only attach one script to an account.

You can detach a script from a smart account or replace an old script with a new script unless the old script forbids it. To detach” a script or replace it with a new one, you will need to send a new SetScript transaction.

An example of creating and deploying an account script

This section provides an example of creating and deploying an account script manually without using client libraries and API libraries.

In this example we will create and deploy a simple account script that checks for transaction multiple signatures (two out of two).

Example Assumptions

In creating this account script, the following is implied:

  1. You have your own node in the Waves Enterprise blockchain network.

  2. You have three addresses generated on the Waves Enterprise blockchain network:

    • 3MxjWXEUcVCeiaEUqNcorB5HxSpLsgJCGxE – Alice’s account,

    • 3MqGVvfgqdqqU6P9mTAsLSxyRoRjrHF18Mf – Bob’s account,

    • 3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X – shared account.

Creating the account script

In this example, the following script is created:

  • The first two lines of the script define 2 base58 encoded public keys for Alice’s and Bob’s addresses.

  • Users then collect 2 public keys in the proofs[0] and proofs[1] fields.

    The account balance is funded by team members, then when 2 out of 2 team members decide to spend the money, they provide their signatures in a single transaction.

  • The smart account script uses the sigVerify function to verify these signatures in proofs, and if two of the two signatures are valid, the transaction is also considered valid; otherwise, the transaction does not pass to the blockchain.

There is no directive in the script, so automatic values will be selected.

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

Converting the script to Base64 format

Use the /utils/script/compile method to compile the script and convert the script to Base64 format.

You can use Swagger for this purpose:

../_images/account_script.png

Or you can use curl to compile the script and convert it to Base64 format:

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"

Attaching the script to the account

To attach the Base64-encoded script to your account, follow these steps:

  1. Prepare 13. SetScript Transaction JSON for signing. Specify the shared account as the sender; set the account script in the script field:

    {
      "type": 13,
      "version": 1,
      "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X",
      "fee": 100000,
      "script": "<script>"
    }
    
  2. Sign the transaction using the /transactions/sign method:

    $ 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'
    

    The method will return JSON ready for broadcasting:

    {
      "type": 13,
      "id": "8w7yauNiENsJP8oDUpVEfiAzyEzMKoXbJEqS26Ht99mg",
      "sender": "3N7H4jTBMKtZfNCY86K2ND1rWcvFsGjDT3X",
      "senderPublicKey": "66xdGznqt2AVLMZRHme9vFPC6cvN4yV95wRWPfTus3Qe",
      "fee": 100000,
      "timestamp": 1525797758819,
      "proofs": [
        "4Ro4e4UrsVkaFbHtu96qZwHAdf8N4TtpjSGik9kRusmmYKCxicdsEqcgQrYden36nurqhY9EBkTKwD499kAi5rxe"
      ],
      "version": 1,
      "script": "<script>"
    }
    
  3. Broadcast the resulting JSON to the blockchain using the POST /transactions/broadcast method:

    $ 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. Check to see if the script has been applied:

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

where <scriptText> is the string representation of the compiled <script> (expression tree).

Now when a transfer is sent from the shared account to another account using the 4. Transfer Transaction, the script will verify if both signatures (of Alice’s and Bob’s accounts) are present in the proofs field. If there is no signature of at least one of the accounts, the script will return the error: “State check failed. Reason: TransactionNotAllowedByScript”, and the transaction will not be published on the network.

See also