github.com/mavryk-network/mvgo@v1.19.9/cmd/tzcompose/README.md (about)

     1  # TzCompose - Tezos Automation Framework
     2  
     3  TzCompose is a tool for defining and running complex transaction sequences on Tezos. With TzCompose, you use a YAML file to configure pipelines from different kinds of tasks and then run a single command to execute these pipelines.
     4  
     5  TzCompose works on all Tezos networks and makes it easy to clone contracts and transaction sequences between them.
     6  
     7  Developers can use TzCompose for
     8  
     9  - smart contract deployment and maintenance
    10  - traffic generation for protocol and application testing
    11  - automating test-case setups
    12  - cloning contract deployments and setup logic between networks
    13  
    14  ## Using TzCompose
    15  
    16  ```sh
    17  go run github.com/mavryk-network/mvgo/cmd/tzcompose [cmd] [flags]
    18  
    19  Env
    20    TZCOMPOSE_BASE_KEY  private key for base account
    21    TZCOMPOSE_API_KEY   API key for RPC and index calls (optional)
    22  
    23  Flags
    24    -f file
    25        configuration file or path (default "tzcompose.yaml")
    26    -file file
    27        configuration file or path (default "tzcompose.yaml")
    28    -resume
    29        continue pipeline execution
    30    -rpc string
    31        Tezos node RPC url (default "https://rpc.tzpro.io")
    32    -h  print help and exit
    33    -v  be verbose (default true)
    34    -vv
    35        debug mode
    36    -vvv
    37        trace mode
    38  ```
    39  
    40  TzCompose can execute configurations from a single file `-f file.yaml`, a single directory `-f ./examples/fa` or all subdirectories `-f ./examples/...` in which cases all yaml files will be read in filesystem order.
    41  
    42  ### Available Commands
    43  
    44  - `clone`: clone transactions starting from the origination of a contract
    45  - `validate`: validate compose file syntax and parameters
    46  - `simulate`: simulate compose file execution against a blockchain node
    47  - `run`: execute compose file(s) sending signed transactions to a blockchain node
    48  - `version`: print version and exit
    49  
    50  TzCompose relies on the Tezos Node RPC and (for clone) on the TzIndex API. Both are publicly available via https://tzpro.io with a free subscription. Export your API key as
    51  
    52  ```sh
    53  export TZCOMPOSE_API_KEY=<your-api-key>
    54  ```
    55  
    56  ### Available Tasks
    57  
    58  - [batch](#batch) - send multiple transactions as single operation
    59  - [call](#call) - send smart contract call
    60  - [delegate](#delegate) - delegate to baker
    61  - [deploy](#deploy) - create smart contract
    62  - [double_endorse](#double-endorse) - force a double endorsement slash
    63  - [double_bake](#double-bake) - force a double bake slash
    64  - [register_baker](#register-baker) - register as baker
    65  - [token_approve](#token-approve) - approve token spender
    66  - [token_revoke](#token-revoke) - revoke token spender
    67  - [token_transfer](#token-transfer) - send token transfer(s)
    68  - [transfer](#transfer) - send tez transfer(s)
    69  - [undelegate](#undelegate) - remove delegation from baker
    70  - [wait](#wait) - wait for condition
    71  - [stake](#stake) - Atlas stake
    72  - [unstake](#unstake) - Atlas unstake
    73  - [finalize_unstake](#finalize_stake) - Atlas finalize unstake
    74  
    75  ### Configuration
    76  
    77  TzCompose YAML files contain different sections to define accounts, variables and pipelines. Pipelines can be composed from different `tasks` which will generate transactions when executed. A compose file may contain multiple pipelines and each pipeline virtually unlimited tasks.
    78  
    79  ```yaml
    80  # Available engines: alpha
    81  version: <engine-version>
    82  
    83  # Specify alias names for mv1 user accounts and optional key id
    84  accounts:
    85    - name: alias
    86      id: 1
    87  
    88  # Specify contract addresses or other data as variables and later
    89  # reference them inside pipelines
    90  variables:
    91    - <name>: KT1...
    92  
    93  # Specify transaction sequences as pipelines. Each sequence consists of tasks
    94  # of a specifed type and corresponding input parameters
    95  pipelines:
    96    <name>:
    97      - task: deploy | call | transfer | register_baker | token_transfer | ...
    98        source: $var | address
    99        destination: $var | address
   100  
   101  ```
   102  
   103  ## How it works
   104  
   105  TzCompose reads config files and runs pipeline tasks in order of appearance. Each task emits a transaction that is signed and broadcast to the network. If successful, the next task is processed. Task configurations may contain static content, reference variables and import data files which makes it easy to orchestrate complex scenarios.
   106  
   107  TzCompose automates wallet/key management and guarantees deterministic account addresses. That way, running the same pipeline on a different network will lead to the same result. (contract addresses are one unfortunate exception).
   108  
   109  TzCompose also stores pipeline state and can resume execution from where it stopped.
   110  
   111  ### Wallets
   112  
   113  TzCompose requires a single funded `base` account to sign and send transactions. Configure the base account by exporting its private key as `TZCOMPOSE_BASE_KEY` environment variable.
   114  
   115  On Flextes sandbox extract the private key with
   116  
   117  ```sh
   118  export TZCOMPOSE_BASE_KEY=`docker exec tezos_sandbox flextesa key-of-name alice | cut -f4 -d, | cut -f2 -d:`
   119  ```
   120  
   121  All other wallet keys are deterministically derived from this `base` key using BIP32. Child keys are identified by their numeric id. You can assign alias names to them in the `accounts` section of a compose file. All child accounts use Ed25519 keys (mv1 addresses).
   122  
   123  > TzCompose does not allow you to specify wallet keys in configuration files. This is a deliberate design choice to prevent accidental leakage of key material into code repositories.
   124  
   125  Keep in mind that when you reuse the same child ids in different compose files, these accounts may have state and history from executing other compose files. Usually this is not a problem, but it may be in certain test scenarios when the account is already a baker or is expected to be empty.
   126  
   127  ### Variables
   128  
   129  TzCompose lets you define variables for common strings and addresses. You can use variables as source, destination and in task arguments. TzCompose defines a few default variables
   130  
   131  * `$base` - base account address
   132  * `$now` - current wall clock time in UTC (you can also add durations like `$now+5m`)
   133  * `$zero` - mv1 zero address (binary all zeros)
   134  * `$burn` - mv1 burn address
   135  
   136  ### Cloning Contracts
   137  
   138  In some cases it is desirable to clone existing contracts and their setup procedure across networks. TzCompose implements a `clone` command which downloads contract code, initial storage and transaction history from an indexer and writes a deployable pipeline into a fresh compose config file.
   139  
   140  TzCompose offers different clone modes which define how and where script, storage and params are stored:
   141  
   142  * `file` stores Micheline data as JSON data in separate files and references them inside the config file
   143  * `url` stores a Tezos RPC URL in inside the config file and fetches data on demand
   144  * `bin` embeds Micheline data as hex string into the config file
   145  * `json` embeds Micheline data as JSON string into the config file
   146  
   147  In either case `clone` attempts to decode storage and call parameters into human readable args. If this fails because args are too complex or a construct is unsupported the fallback is to use pre-encoded Micheline data.
   148  
   149  A single clone call is often enough to get a working copy of a contract. Some contracts may require admin permissions or other changes to initial storage. Then it is necessary to overwrite some arguments. With args available just replace any fixed admin address with `$base` (or `$var` for child accounts) and add/remove arg contents directly in the compose file as necessary.
   150  
   151  Without args you can [patch](#patching-micheline) Micheline encoded data to edit the contents of specific nodes.
   152  
   153  
   154  **Clone Example**
   155  
   156  Here we clone the Tether contract and 2 transactions following its origination which generates a `tzcompose.yaml` file along with several files containing contract source and call paramaters.
   157  
   158  ```sh
   159  # clone Tether FA2 contract from mainnet
   160  tzcompose clone -contract KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o -name tether -n 2
   161  ```
   162  
   163  Then open `tzcompose.yaml` and edit its admin accounts. For brevity we show relevant lines only, the original file is longer:
   164  ```yaml
   165  pipelines:
   166    tether:
   167      - task: deploy
   168        script:
   169          storage:
   170            args:
   171              administrators:
   172                $base: "1"     # <- replace with $base variable here
   173      - task: call
   174        params:
   175          entrypoint: mint
   176          args:
   177            - owner: $base     # <- replace with $base variable here
   178  ```
   179  
   180  
   181  ### Micheline Arguments
   182  
   183  Initial storage and contract call parameters must be Micheline encoded, but it's a pain to read and write this format. TzCompose allows you to either use pre-encoded files, binary blobs or YAML key/value arguments.
   184  
   185  When using arguments keep in mind that all fields are required, even if their value is empty or default. If args are too complex and you have to work with pre-encoded data you can still use [patch](#patching-micheline) for replacing contents as described below.
   186  
   187  Both YAML args and patch use variable replacement for addresses, strings and timestamps.
   188  
   189  ```yaml
   190  args:
   191    # Keys must always be strings
   192    key: ...  # Regular keys don't require quites
   193    "0": ...  # Use quotes when the key is numeric
   194    "": "..." # Empty string (for metadata)
   195  
   196    # Integers up to int64 can be written without quotes, bigints require quotes
   197    val: 1
   198    val: "1"
   199  
   200    # Strings can be written without quotes as long as they don't contain a reserved
   201    # yaml character, yaml multiline strings are supported as well
   202    val: some_string
   203    val: some string
   204    val: "some string"
   205    val: >
   206      a string
   207      that spans
   208      multiple lines
   209  
   210    # Sets and lists must be written as YAML arrays
   211    my_set: null # empty
   212    my_set: []   # empty
   213    my_set:
   214      - val1
   215      - val2
   216  
   217    # Maps and initial bigmap storage must be written as YAML map
   218    my_map: null # empty
   219    my_map: {}   # empty
   220    my_map:
   221      key_1: val_1 # map keys must be unique
   222      key_2: val_2
   223      "mv1...,1": val # write Micheline pair keys as comma separated strings
   224  
   225    # Null (or Unit) as well as empty optionals (None) are written as null
   226    option: null
   227  
   228    # Union types must contain the nested name of the chosen branch
   229    # i.e. FA2 update_operators calls would look like
   230    - add_operator:
   231      owner: $account-1
   232      operator: $account-2
   233      token_id: 0
   234  
   235    # For contracts without named storage or paramaeters, use the numeric position
   236    "0": val # first arg
   237    "1": val # second arg
   238  ```
   239  
   240  ### Specifying Data Sources
   241  
   242  TzCompose can read Micheline data from different sources. Compilers typically store code and storage in JSON files, but when cloning we may want to embed everything into a the config file to make it self-sufficient or even pull data from a URL when running the pipeline.
   243  
   244  Below is a list of available options which work for scripts and call params:
   245  
   246  ```yaml
   247  - task: deploy
   248    script:
   249      code:
   250        # Read from a file relative to the config file's directory
   251        file:
   252        # Read from a remote URL
   253        url:
   254        # Use the embedded value (either JSON string or hex string)
   255        value:
   256      storage:
   257        # Read from a file relative to the config file's directory
   258        file:
   259        # Read from a remote URL
   260        url:
   261        # Use the embedded value (either JSON string or hex string)
   262        value:
   263        # Replace specific contents read from file, url or value
   264        patch:
   265        # Use human readable arguments
   266        args:
   267  
   268  - task: call
   269    params:
   270      # Entrypoint name is always required
   271      entrypoint: name
   272      # Read from a file relative to the config file's directory
   273      file:
   274      # Read from a remote URL
   275      url:
   276      # Use the embedded value (either JSON string or hex string)
   277      value:
   278      # Replace specific contents read from file, url or value
   279      patch:
   280      # Use human readable arguments
   281      args:
   282  ```
   283  
   284  ### Referencing Files
   285  
   286  To increase flexibility when working with different Michelson compilers TzCompose provides a few ways to load data from files:
   287  
   288  ```yaml
   289  - task: deploy
   290    script:
   291      code:
   292        # When the file contains only the code segment
   293        file: code.json
   294        # When code is nested like with Ligo we can append a json-path
   295        file: code.json#michelson
   296      storage:
   297        # Read the entire file
   298        file: storage.json
   299        # Extract with json-path from file
   300        file: script.json#storage
   301        # Inside args reference files as `@filename#json-path`
   302        # (may need quotes since @ is reserved for future use in yaml)
   303        args:
   304          func: "@lambdas.json#0"
   305  ```
   306  
   307  ### Patching Micheline
   308  
   309  In case you only have storage or parameters in files or binary blobs you may want to patch certain fields inside. To do this add one or more `patch` sections:
   310  
   311  ```yaml
   312  - task: deploy
   313    script:
   314      storage:
   315        # Specifies the source where to read the original full storage primitive tree from
   316        file: storage.json
   317        # Patch takes a list of instructions and executes them in order
   318        patch:
   319        -
   320          # Key defines a (nested) label from the Micheline type tree where to
   321          # patch contents in the value tree. this works in many cases, but if
   322          # it doesn't (because type is too complex like nested maps in maps etc)
   323          # you can use path below
   324          key: manager
   325          # Path is an alternative to key. it specifies the exact path in the
   326          # Micheline value tree to replace. Path segments are always numeric.
   327          path: 0/0/1
   328          # Type defines the primitive type to replace. It is used to correctly
   329          # parse and type-check the value and to produce the correct Micheline
   330          # primitive.
   331          type: address
   332          # Value contains the contents we want to replace with. It is typically
   333          # a string, int or variable.
   334          value: $base
   335          # Optimized defines if the output prim is in optimized form or not.
   336          # This applies to addresses, timestamps, keys and signatures.
   337          optimized: false
   338  ```
   339  
   340  ### Error Handling
   341  
   342  Per default, tzcompose will stop executing all pipelines when the first error occurs. This may be a configuration, network communication or transaction state error. Sometimes it is useful to skip non-critical expected errors such as re-activating an already active baker. Since tzcompose does not know what is critical to your pipeline and what not, you can use the `on-error` argument to specify how an error is handled.
   343  
   344  ```yaml
   345  - task: transfer
   346    on-error: ignore # allowed values: ignore, warn, fail
   347  ```
   348  
   349  ## Task Reference
   350  
   351  Tasks define a single action which typically emits an on-chain transaction. The transaction will be signed by the account specified in the `source` field. If source is empty it defaults to `$base`. Most tasks also define a `destination` which is either target of a call or receiver of a transfer or other activity.
   352  
   353  Continue reading for a list of available tasks in alphabetic order:
   354  
   355  ### Batch
   356  
   357  Batch tasks combine multiple actions into a single signed transaction. They are useful to save time and gas, but also to atomically perform events that would otherwise be less secure (like granting and revoking token allowances). Almost all other tasks can be nested inside a batch.
   358  
   359  ```yaml
   360  # Spec
   361  task: batch
   362  source: $var
   363  contents:
   364  - task: transfer
   365    destination: $var | string
   366    amount: number
   367  - task: transfer
   368    destination: $var | string
   369    amount: number
   370  ```
   371  
   372  ### Call
   373  
   374  Call is a generic way to send smart contract calls. Calls require a destination contract, an entrypoint and call arguments. Entrypoint and argument types must match the contract interface. TzCompose checks before signing a transaction whether the entrypoint exists and arguments are valid.
   375  
   376  ```yaml
   377  # Spec
   378  task: call
   379  source: $var
   380  destination: $var | string
   381  params:
   382    entrypoint: name
   383    args:
   384    file:
   385    url:
   386    value:
   387  ```
   388  
   389  ### Delegate
   390  
   391  Delegate delegates `source`'s full spendable balance to the selected `destination` baker. On Tezos this balance remains liquid.
   392  
   393  ```yaml
   394  # Spec
   395  task: delegate
   396  source: $var
   397  destination: $var | string
   398  ```
   399  
   400  ### Deploy
   401  
   402  Deploy creates a new smart contract from `code` and `storage` and on success makes the contract's address available as variable (use `alias` to define the variable name).
   403  
   404  ```yaml
   405  # Spec
   406  task: deploy
   407  # required, alias defines the variable name under which the new contract
   408  # address is made available to subsequent tasks and pipelines
   409  alias: string
   410  # optional tez amount to send to the new contract
   411  amount: number
   412  # specify contract script
   413  script:
   414    # you may use a single source for both code and storage if available
   415    # only one of url, file, value is permitted
   416    url:
   417    file:
   418    value:
   419    # or you can specify independent code and storage sections
   420    # only one of url, file, value is permitted
   421    code:
   422      file:
   423      url:
   424      value:
   425    # add an independent storage section when you use args or patch or
   426    # when you need to load data from a different source than code
   427    # only one of args, url, file, value is permitted
   428    # use patch for replacing content loaded via url, file or value
   429    storage:
   430      args:
   431      file:
   432      url:
   433      value:
   434      patch:
   435  ```
   436  
   437  ### Double Endorse
   438  
   439  Produces a fake double-endorsement which slashes the baker in `destination` and awards denunciation rewards to the baker who includes this operation. The destination must be registered as baker and a private key must be available for signing. The slashed baker must have endorsements rights and this task waits until a block with such rights is baked.
   440  
   441  To successfully execute this task you need to sufficiently fund and register the baker and then wait a few cycles for rights to activate. Note that on sandboxes the $alice key is not the sandbox baker. To lookup the actual baker key, docker exec into the sandbox and search for the `secret_keys` file in the Tezos client dir.
   442  
   443  ```yaml
   444  # Spec
   445  task: double_endorse
   446  destination: $var # <- this baker is slashed
   447  ```
   448  
   449  ### Double Bake
   450  
   451  Produces a fake double-bake which slashes the baker in `destination` and awards denunciation rewards to the baker who includes this operation. The destination must be registered as baker and a private key must be available for signing. The slashed baker must have at least one round zero baking rights. The task waits until a block with such right is baked and then sends a double baking evidence with two fake (random) payload hashes.
   452  
   453  To successfully execute this task you need to sufficiently fund and register the baker and then wait a few cycles for rights to activate. Note that on sandboxes the $alice key is not the sandbox baker. To lookup the actual baker key, docker exec into the sandbox and search for the `secret_keys` file in the Tezos client dir.
   454  
   455  ```yaml
   456  # Spec
   457  task: double_bake
   458  destination: $var # <- this baker is slashed
   459  ```
   460  
   461  ### Register Baker
   462  
   463  Registers the source account as baker on the network by sending a self-delegation. Registration is idempotent. In case a baker becomes inactive it can be easily reactivated by registering it again.
   464  
   465  ```yaml
   466  # Spec
   467  task: register_baker
   468  source: $var
   469  ```
   470  
   471  ### Token Approve
   472  
   473  Approve specifies that a `spender` account is allowed to transfer a token amount owned by `source` in the token ledger contract `destination`. FA1.2 supports max amount, but no token id. FA2 has token ids, but you can only allow to spend everything or nothing.
   474  
   475  Approve is a convenience task that hides details of different standards. For full control use a `call` task.
   476  
   477  ```yaml
   478  # Spec
   479  task: token_approve
   480  source: $var
   481  destination: $var | string # token ledger
   482  args:
   483    standard: fa12 | fa2
   484    token_id: number # optional, fa2 only
   485    spender: $var | string
   486    amount: number # required for fa12, ignored for fa2
   487  ```
   488  
   489  ### Token Revoke
   490  
   491  Revoke is the opposite of approve and withdraws a `spender`'s right to transfer tokens in ledger `destination`. Revoke is a convenience task that hides details of different standards. For full control use a `call` task.
   492  
   493  
   494  ```yaml
   495  # Spec
   496  task: token_revoke
   497  source: $var
   498  destination: $var | string # token ledger
   499  args:
   500    standard: fa12 | fa2
   501    token_id: number # optional, fa2 only
   502    spender: $var | string
   503  ```
   504  
   505  ### Token Transfer
   506  
   507  Transfers an `amount` of tokens with `token_id` owned by account `from` in ledger `destination` to another account `to`. `source` must either be the same as `from` or must be approved as spender.
   508  
   509  ```yaml
   510  # Spec
   511  task: token_transfer
   512  source: $var
   513  destination: $var | string # token ledger
   514  args:
   515    standard: fa12 | fa2
   516    token_id: number # optional, fa2 only
   517    from: $var | string
   518    to: $var | string
   519    amount: number
   520    # fa2 only: specify multiple receivers and multiple token ids
   521    # this is mutually exclusive with token_id, to and amount above
   522    receivers:
   523      token_id: number
   524      to: $var | string
   525      amount: number
   526  
   527  ```
   528  
   529  ### Transfer
   530  
   531  Transfers an `amount` of tez from `source` to `destination`.
   532  
   533  ```yaml
   534  # Spec
   535  task: transfer
   536  source: $var
   537  destination: $var | string
   538  amount: number
   539  ```
   540  
   541  ### Undelegate
   542  
   543  Undelegate removes the `source`'s delegation from its current baker.
   544  
   545  ```yaml
   546  # Spec
   547  task: undelegate
   548  source: $var
   549  ```
   550  
   551  ### Wait
   552  
   553  Wait stops pipeline execution for a defined amount of blocks, time or cycles. It is useful to wait for protocol events (at cycle and) or allow for some traffic getting generated by a
   554  second tzcompose instance. Wait is not able to syncronize between pipelines though.
   555  
   556  ```yaml
   557  # Spec
   558  task: wait
   559  for: cycle | block | time
   560  value: N | +N
   561  ```
   562  
   563  ```yaml
   564  # wait for next cycle
   565  type: wait
   566  for: cycle
   567  value: +1
   568  
   569  # wait 10 blocks
   570  type: wait
   571  for: block
   572  value: +10
   573  
   574  # wait until tomorrow
   575  type: wait
   576  for: time
   577  value: +24h
   578  ```
   579  
   580  ```yaml
   581  pipelines:
   582    transfer-and-wait:
   583    - task: transfer
   584      destination: $account-1
   585      amount: 10_000_000
   586    - task: wait
   587      for: block
   588      value: +10
   589    - task: transfer
   590      destination: $account-2
   591      amount: 10_000_000
   592  ```
   593  
   594  ### Stake
   595  
   596  Lets the `source` stake `amount` with its current baker. Source must be delegated and staking must be enabled in the protocol for this to succeed. This operation takes no destination because the operation is sent to source.
   597  
   598  ```yaml
   599  # Spec
   600  task: stake
   601  source: $var
   602  amount: number
   603  ```
   604  
   605  ### Unstake
   606  
   607  Lets the `source` request unstake of `amount` from the current baker. Source must be staking for this to succeed. This operation takes no destination because the operation is sent to source.
   608  
   609  ```yaml
   610  # Spec
   611  task: unstake
   612  source: $var
   613  amount: number
   614  ```
   615  
   616  ### Finalize Unstake
   617  
   618  Lets `source` request to pay out unstaked unfrozen funds back to spendable balance. This operation takes no amount and no destination. All available funds are sent.
   619  
   620  ```yaml
   621  # Spec
   622  task: finalize_unstake
   623  source: $var
   624  ```