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 ```