From d57d3a1ba73e89a84e793e49bb4c85e0ce429e16 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Thu, 31 Aug 2023 13:16:45 -0400 Subject: [PATCH 01/35] feat: upgrade to using clarinet crate to parse config (#46) --- Cargo.lock | 22 +- Cargo.toml | 11 +- README.md | 8 +- examples/new-network-minimal.example.json | 61 --- examples/new-network.example.json | 431 +++++++++++++++--- src/config.rs | 413 ++++++----------- src/lib.rs | 102 ++--- src/tests/fixtures/deployment-plan.yaml | 99 +++- src/tests/fixtures/network-manifest.yaml | 208 +++++++-- src/tests/fixtures/project-manifest.yaml | 33 +- src/tests/fixtures/stacks-devnet-config.json | 420 ++++++++++++++--- ...tcoind-chain-coordinator-pod.template.yaml | 3 +- templates/stacks-devnet-api.template.yaml | 4 +- 13 files changed, 1221 insertions(+), 594 deletions(-) delete mode 100644 examples/new-network-minimal.example.json diff --git a/Cargo.lock b/Cargo.lock index 8795095..207604a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,9 +399,9 @@ dependencies = [ [[package]] name = "clarinet-deployments" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d559d56b906d2af1a6f4552607e059d9383f5961f062c62e8df743755b59a86f" +checksum = "9ad2b850465d600a43e35e24a4a250504f946757bfcdd57d38ce012ffee2aa29" dependencies = [ "base58 0.2.0", "bitcoin", @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "clarinet-files" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1464355d97840afd89fcc04c4361a4d06200de1ddf4a311b649744c7e67d0155" +checksum = "1fc42d784155c3dc4f578f14f41fd7fa4f01c3354a8b5a93e5cbe966cf002cce" dependencies = [ "bip39", "bitcoin", @@ -434,6 +434,7 @@ dependencies = [ "libsecp256k1 0.7.1", "serde", "serde_derive", + "serde_json", "tiny-hderive", "toml", "url", @@ -453,9 +454,9 @@ dependencies = [ [[package]] name = "clarity-repl" -version = "1.7.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2d096ff86def33801c5ad13dc222befa42118326a80cfc1cf34d9ee1abc70b" +checksum = "0b21c5cf2058527e435fbfb0040dd715190989a020c9ae7bcebce4cf4b3aabb9" dependencies = [ "ansi_term", "atty", @@ -2528,9 +2529,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "indexmap 2.0.0", "itoa", @@ -2840,7 +2841,6 @@ dependencies = [ name = "stacks-devnet-api" version = "0.1.0" dependencies = [ - "base64 0.21.2", "chainhook-types", "clarinet-deployments", "clarinet-files", @@ -2868,9 +2868,9 @@ dependencies = [ [[package]] name = "stacks-rpc-client" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "417254594e89666f68c76267a620d3751ae6604e39c97f3abb57a845390bc1a0" +checksum = "55ed9348051a94c97b7173792fd8d1308a2e89dada5da920dd22ac629464e6cb" dependencies = [ "clarity-repl", "hmac 0.12.1", diff --git a/Cargo.toml b/Cargo.toml index 895c2fa..befee83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,11 @@ http-body = "0.4.5" hiro-system-kit = {version = "0.1.0", features = ["log"]} strum_macros = "0.24.3" strum = "0.24.1" -base64 = "0.21.2" -clarity-vm = "2.1.1" -clarity-repl = "1.6.4" -clarinet-files = "1.0.0" -chainhook-types = "1.0.1" -clarinet-deployments = "1.0.1" +clarity-vm = "2.1.1" +clarity-repl = "1.7.2" +clarinet-files = {version = "1.0.2" } +chainhook-types = "1.0" +clarinet-deployments = {version = "1.0.2" } toml = "0.5.9" [dev-dependencies] diff --git a/README.md b/README.md index 1d472d5..cc2b61d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,13 @@ The `Config.toml` at the root directory of the project can be used to control so - `allowed_methods` - this setting is an array of strings that sets what HTTP methods can be made to this server. ## Deploying the Stable Version -In your terminal, run +First, you'll need to use your docker credentials to be able to pull the docker image. To create the needed secret, in your terminal run: +``` +kubectl create secret --namespace devnet docker-registry stacks-devnet-api-secret --docker-server=https://index.docker.io/v1/ --docker-username= --docker-email= --docker-password= +``` +and enter in the details for a docker user that has access to the `hirosystems/stacks-devnet-api` image. + +Then, in your terminal, run ``` ./scripts/deploy-api.sh ``` diff --git a/examples/new-network-minimal.example.json b/examples/new-network-minimal.example.json deleted file mode 100644 index 05f70d4..0000000 --- a/examples/new-network-minimal.example.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "namespace": "test-namespace", - "bitcoin_node_username": "test-username", - "bitcoin_node_password": "test-password", - "project_manifest": { - "name": "px", - "description": "my description", - "authors": ["test1", "test2"], - "requirements": ["test1", "test2"] - }, - "accounts": [ - { - "name": "deployer", - "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", - "balance": 100000000000000, - "stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - { - "name": "wallet_1", - "mnemonic": "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild", - "balance": 100000000000000, - "stx_address": "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5" - } - ], - "deployment_plan": { - "id": 0, - "name": "Devnet deployment", - "network": "devnet", - "stacks-node": "http://localhost:20443", - "bitcoin-node": "http://px-devnet:px-devnet@localhost:18443", - "plan": { - "batches": [ - { - "id": 0, - "transactions": [ - { - "contract-publish": { - "contract-name": "px", - "expected-sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "cost": 18060, - "path": "contracts/px.clar", - "anchor-block-only": true, - "clarity-version": 2 - } - } - ], - "epoch": "2.1" - } - ] - } - }, - "contracts": [ - { - "name": "px", - "source": "XG47OyB0aXRsZTogcHhcbjs7IHZlcnNpb246XG47OyBzdW1tYXJ5OlxuOzsgZGVzY3JpcHRpb246IEFsbG93cyB1c2VycyB0byBwYXkgdG8gdXBkYXRlIGRhdGEgaW4gYSBtYXRyaXguIFxuOzsgIEVhY2ggbWF0cml4IHZhbHVlIG11c3QgYmUgYSBoZXhhZGVjaW1hbCB2YWx1ZSBmcm9tIDB4MDAwMDAwIHRvIDB4ZmZmZmZmLCByZXByZXNlbnRpbmcgYSBjb2xvciB0byBiZSBkaXNwbGF5ZWQgb24gYSBncmlkIGluIGEgd2ViIHBhZ2UuIFxuOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuXG5cbjs7IHRyYWl0c1xuOztcblxuOzsgdG9rZW4gZGVmaW5pdGlvbnNcbjs7IFxuXG47OyBjb25zdGFudHNcbjs7XG4oZGVmaW5lLWNvbnN0YW50IE1BWF9MT0MgdTEwMClcbihkZWZpbmUtY29uc3RhbnQgTUFYX1ZBTCAweGZmZmZmZilcbihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMClcbihkZWZpbmUtY29uc3RhbnQgQUxMX0xPQ1MgKGxpc3QgdTAgdTEgdTIgdTMgdTQgdTUgdTYgdTcgdTggdTkgdTEwIHUxMSB1MTIgdTEzIHUxNCB1MTUgdTE2IHUxNyB1MTggdTE5IHUyMCB1MjEgdTIyIHUyMyB1MjQgdTI1IHUyNiB1MjcgdTI4IHUyOSB1MzAgdTMxIHUzMiB1MzMgdTM0IHUzNSB1MzYgdTM3IHUzOCB1MzkgdTQwIHU0MSB1NDIgdTQzIHU0NCB1NDUgdTQ2IHU0NyB1NDggdTQ5IHU1MCB1NTEgdTUyIHU1MyB1NTQgdTU1IHU1NiB1NTcgdTU4IHU1OSB1NjAgdTYxIHU2MiB1NjMgdTY0IHU2NSB1NjYgdTY3IHU2OCB1NjkgdTcwIHU3MSB1NzIgdTczIHU3NCB1NzUgdTc2IHU3NyB1NzggdTc5IHU4MCB1ODEgdTgyIHU4MyB1ODQgdTg1IHU4NiB1ODcgdTg4IHU4OSB1OTAgdTkxIHU5MiB1OTMgdTk0IHU5NSB1OTYgdTk3IHU5OCB1OTkpKVxuOzsgZGF0YSB2YXJzXG47O1xuXG47OyBkYXRhIG1hcHNcbjs7XG4oZGVmaW5lLW1hcCBwaXhlbHMgdWludCAoYnVmZiAzKSlcblxuOzsgcHVibGljIGZ1bmN0aW9uc1xuOztcbihkZWZpbmUtcHVibGljIChzZXQtdmFsdWUtYXQgKGxvYyB1aW50KSAodmFsdWUgKGJ1ZmYgMykpKSBcbiAgICAoYmVnaW4gXG4gICAgICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgICAgICAoZXJyIFwiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy5cIilcbiAgICAgICAgICAgIChpZiAoPiB2YWx1ZSBNQVhfVkFMKVxuICAgICAgICAgICAgICAgIChlcnIgXCJWYWx1ZSBtdXN0IGJlIGxlc3MgdGhhbiAweGZmZmZmZi5cIilcbiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTClcbiAgICAgICAgICAgICAgICAgICAgKGVyciBcIlZhbHVlIG11c3QgYmUgZ3JlYXRlciB0aGFuIDB4MDAwMDAwLlwiKVxuICAgICAgICAgICAgICAgICAgICAob2sgKG1hcC1zZXQgcGl4ZWxzIGxvYyB2YWx1ZSkpXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKVxuICAgICAgICApXG4gICAgKVxuKVxuOzsgcmVhZCBvbmx5IGZ1bmN0aW9uc1xuOztcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdldC12YWx1ZS1hdCAobG9jIHVpbnQpKVxuICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgIChlcnIgXCJPdXQgb2YgYm91bmRzLlwiKVxuICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSlcbiAgICApXG4pXG5cbihkZWZpbmUtcmVhZC1vbmx5IChnZXQtYWxsKSBcbiAgICAobWFwIGdldC12YWx1ZS1hdCBBTExfTE9DUylcbilcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdlbmVzaXMtdGltZSAoaGVpZ2h0IHVpbnQpKVxuICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpXG4pXG47OyBwcml2YXRlIGZ1bmN0aW9uc1xuOztcbg==", - "clarity_version": 2, - "epoch": 2.1, - "deployer": null - } - ] -} diff --git a/examples/new-network.example.json b/examples/new-network.example.json index 08de413..1fcf0f5 100644 --- a/examples/new-network.example.json +++ b/examples/new-network.example.json @@ -1,80 +1,399 @@ { - "namespace": "test-namespace1", - "stacks_node_wait_time_for_microblocks": 50, - "stacks_node_first_attempt_time_ms": 500, - "stacks_node_subsequent_attempt_time_ms": 1000, - "bitcoin_node_username": "test-username", - "bitcoin_node_password": "test-password", - "miner_mnemonic": "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild", - "miner_derivation_path": "m/44'/5757'/0'/0/0", - "miner_coinbase_recipient": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", - "faucet_mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", - "faucet_derivation_path": "m/44'/5757'/0'/0/0", - "bitcoin_controller_block_time": 0, - "bitcoin_controller_automining_disabled": true, - "disable_bitcoin_explorer": true, - "disable_stacks_explorer": true, + "namespace": "test-namespace", "disable_stacks_api": false, - "epoch_2_0": 100, - "epoch_2_05": 102, - "epoch_2_1": 106, - "epoch_2_2": 120, - "pox_2_activation": 112, - "pox_2_unlock_height": 112, - "project_manifest": { - "name": "px", - "description": "my description", - "authors": ["test1", "test2"], - "requirements": ["test1", "test2"] - }, - "accounts": [ - { - "name": "deployer", - "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", - "balance": 100000000000000, - "stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - { - "name": "wallet_1", - "mnemonic": "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild", - "balance": 100000000000000, - "stx_address": "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5" - } - ], "deployment_plan": { "id": 0, "name": "Devnet deployment", "network": "devnet", - "stacks-node": "http://localhost:20443", - "bitcoin-node": "http://px-devnet:px-devnet@localhost:18443", + "stacks_node": "http://localhost:20443", + "bitcoin_node": "http://px-devnet:px-devnet@localhost:18443", + "genesis": null, "plan": { "batches": [ { "id": 0, "transactions": [ { - "contract-publish": { - "contract-name": "px", - "expected-sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "RequirementPublish": { + "contract_id": { + "name": "nft-trait", + "issuer": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT" + }, + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "KGRlZmluZS10cmFpdCBuZnQtdHJhaXQKICAoCiAgICA7OyBMYXN0IHRva2VuIElELCBsaW1pdGVkIHRvIHVpbnQgcmFuZ2UKICAgIChnZXQtbGFzdC10b2tlbi1pZCAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyBVUkkgZm9yIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgdG9rZW4KICAgIChnZXQtdG9rZW4tdXJpICh1aW50KSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctYXNjaWkgMjU2KSkgdWludCkpCgogICAgIDs7IE93bmVyIG9mIGEgZ2l2ZW4gdG9rZW4gaWRlbnRpZmllcgogICAgKGdldC1vd25lciAodWludCkgKHJlc3BvbnNlIChvcHRpb25hbCBwcmluY2lwYWwpIHVpbnQpKQoKICAgIDs7IFRyYW5zZmVyIGZyb20gdGhlIHNlbmRlciB0byBhIG5ldyBwcmluY2lwYWwKICAgICh0cmFuc2ZlciAodWludCBwcmluY2lwYWwgcHJpbmNpcGFsKSAocmVzcG9uc2UgYm9vbCB1aW50KSkKICApCik=", + "clarity_version": "Clarity1", + "cost": 4670, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.clar" + } + } + } + ], + "epoch": "2.0" + }, + { + "id": 1, + "transactions": [ + { + "RequirementPublish": { + "contract_id": { + "issuer": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT", + "name": "sip-010-trait-ft-standard" + }, + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "KGRlZmluZS10cmFpdCBzaXAtMDEwLXRyYWl0CiAgKAogICAgOzsgVHJhbnNmZXIgZnJvbSB0aGUgY2FsbGVyIHRvIGEgbmV3IHByaW5jaXBhbAogICAgKHRyYW5zZmVyICh1aW50IHByaW5jaXBhbCBwcmluY2lwYWwgKG9wdGlvbmFsIChidWZmIDM0KSkpIChyZXNwb25zZSBib29sIHVpbnQpKQoKICAgIDs7IHRoZSBodW1hbiByZWFkYWJsZSBuYW1lIG9mIHRoZSB0b2tlbgogICAgKGdldC1uYW1lICgpIChyZXNwb25zZSAoc3RyaW5nLWFzY2lpIDMyKSB1aW50KSkKCiAgICA7OyB0aGUgdGlja2VyIHN5bWJvbCwgb3IgZW1wdHkgaWYgbm9uZQogICAgKGdldC1zeW1ib2wgKCkgKHJlc3BvbnNlIChzdHJpbmctYXNjaWkgMzIpIHVpbnQpKQoKICAgIDs7IHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdXNlZCwgZS5nLiA2IHdvdWxkIG1lYW4gMV8wMDBfMDAwIHJlcHJlc2VudHMgMSB0b2tlbgogICAgKGdldC1kZWNpbWFscyAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyB0aGUgYmFsYW5jZSBvZiB0aGUgcGFzc2VkIHByaW5jaXBhbAogICAgKGdldC1iYWxhbmNlIChwcmluY2lwYWwpIChyZXNwb25zZSB1aW50IHVpbnQpKQoKICAgIDs7IHRoZSBjdXJyZW50IHRvdGFsIHN1cHBseSAod2hpY2ggZG9lcyBub3QgbmVlZCB0byBiZSBhIGNvbnN0YW50KQogICAgKGdldC10b3RhbC1zdXBwbHkgKCkgKHJlc3BvbnNlIHVpbnQgdWludCkpCgogICAgOzsgYW4gb3B0aW9uYWwgVVJJIHRoYXQgcmVwcmVzZW50cyBtZXRhZGF0YSBvZiB0aGlzIHRva2VuCiAgICAoZ2V0LXRva2VuLXVyaSAoKSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctdXRmOCAyNTYpKSB1aW50KSkKICApCik=", + "clarity_version": "Clarity1", + "cost": 8390, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.clar" + } + } + } + ], + "epoch": "2.05" + }, + { + "id": 2, + "transactions": [ + { + "RequirementPublish": { + "contract_id": { + "issuer": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9", + "name": "subnet-traits-v1" + }, + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "OzsgSW4gb3JkZXIgdG8gc3VwcG9ydCB3aXRoZHJhd2luZyBhbiBhc3NldCB0aGF0IHdhcyBtaW50ZWQgb24gYSBzdWJuZXQsIHRoZQo7OyBMMSBjb250cmFjdCBtdXN0IGltcGxlbWVudCB0aGlzIHRyYWl0LgooZGVmaW5lLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQKICAoCiAgICA7OyBQcm9jZXNzIGEgd2l0aGRyYXdhbCBmcm9tIHRoZSBzdWJuZXQgZm9yIGFuIGFzc2V0IHdoaWNoIGRvZXMgbm90IHlldAogICAgOzsgZXhpc3Qgb24gdGhpcyBuZXR3b3JrLCBhbmQgdGh1cyByZXF1aXJlcyBhIG1pbnQuCiAgICAobWludC1mcm9tLXN1Ym5ldAogICAgICAoCiAgICAgICAgdWludCAgICAgICA7OyBhc3NldC1pZCAoTkZUKSBvciBhbW91bnQgKEZUKQogICAgICAgIHByaW5jaXBhbCAgOzsgc2VuZGVyCiAgICAgICAgcHJpbmNpcGFsICA7OyByZWNpcGllbnQKICAgICAgKQogICAgICAocmVzcG9uc2UgYm9vbCB1aW50KQogICAgKQogICkKKQ==", + "clarity_version": "Clarity2", + "cost": 4810, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1.clar" + } + } + }, + { + "RequirementPublish": { + "contract_id": { + "issuer": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9", + "name": "subnet-v1-2" + }, + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "OzsgVGhlIC5zdWJuZXQgY29udHJhY3QKCihkZWZpbmUtY29uc3RhbnQgQ09OVFJBQ1RfQUREUkVTUyAoYXMtY29udHJhY3QgdHgtc2VuZGVyKSkKCjs7IEVycm9yIGNvZGVzCihkZWZpbmUtY29uc3RhbnQgRVJSX0JMT0NLX0FMUkVBRFlfQ09NTUlUVEVEIDEpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUlORVIgMikKKGRlZmluZS1jb25zdGFudCBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQgMykKKGRlZmluZS1jb25zdGFudCBFUlJfVFJBTlNGRVJfRkFJTEVEIDQpCihkZWZpbmUtY29uc3RhbnQgRVJSX0RJU0FMTE9XRURfQVNTRVQgNSkKKGRlZmluZS1jb25zdGFudCBFUlJfQVNTRVRfQUxSRUFEWV9BTExPV0VEIDYpCihkZWZpbmUtY29uc3RhbnQgRVJSX01FUktMRV9ST09UX0RPRVNfTk9UX01BVENIIDcpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QgOCkKKGRlZmluZS1jb25zdGFudCBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCA5KQooZGVmaW5lLWNvbnN0YW50IEVSUl9WQUxJREFUSU9OX0ZBSUxFRCAxMCkKOzs7IFRoZSB2YWx1ZSBzdXBwbGllZCBmb3IgYHRhcmdldC1jaGFpbi10aXBgIGRvZXMgbm90IG1hdGNoIHRoZSBjdXJyZW50IGNoYWluIHRpcC4KKGRlZmluZS1jb25zdGFudCBFUlJfSU5WQUxJRF9DSEFJTl9USVAgMTEpCjs7OyBUaGUgY29udHJhY3Qgd2FzIGNhbGxlZCBiZWZvcmUgcmVhY2hpbmcgdGhpcy1jaGFpbiBoZWlnaHQgcmVhY2hlcyAxLgooZGVmaW5lLWNvbnN0YW50IEVSUl9DQUxMRURfVE9PX0VBUkxZIDEyKQooZGVmaW5lLWNvbnN0YW50IEVSUl9NSU5UX0ZBSUxFRCAxMykKKGRlZmluZS1jb25zdGFudCBFUlJfQVRURU1QVF9UT19UUkFOU0ZFUl9aRVJPX0FNT1VOVCAxNCkKKGRlZmluZS1jb25zdGFudCBFUlJfSU5fQ09NUFVUQVRJT04gMTUpCjs7IFRoZSBjb250cmFjdCBkb2VzIG5vdCBvd24gdGhpcyBORlQgdG8gd2l0aGRyYXcgaXQuCihkZWZpbmUtY29uc3RhbnQgRVJSX05GVF9OT1RfT1dORURfQllfQ09OVFJBQ1QgMTYpCihkZWZpbmUtY29uc3RhbnQgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQgMzApCgo7OyBNYXAgZnJvbSBTdGFja3MgYmxvY2sgaGVpZ2h0IHRvIGJsb2NrIGNvbW1pdAooZGVmaW5lLW1hcCBibG9jay1jb21taXRzIHVpbnQgKGJ1ZmYgMzIpKQo7OyBNYXAgcmVjb3JkaW5nIHdpdGhkcmF3YWwgcm9vdHMKKGRlZmluZS1tYXAgd2l0aGRyYXdhbC1yb290cy1tYXAgKGJ1ZmYgMzIpIGJvb2wpCjs7IE1hcCByZWNvcmRpbmcgcHJvY2Vzc2VkIHdpdGhkcmF3YWwgbGVhdmVzCihkZWZpbmUtbWFwIHByb2Nlc3NlZC13aXRoZHJhd2FsLWxlYXZlcy1tYXAgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogKGJ1ZmYgMzIpLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogKGJ1ZmYgMzIpIH0gYm9vbCkKCjs7IHByaW5jaXBhbCB0aGF0IGNhbiBjb21taXQgYmxvY2tzCihkZWZpbmUtZGF0YS12YXIgbWluZXIgcHJpbmNpcGFsIHR4LXNlbmRlcikKOzsgcHJpbmNpcGFsIHRoYXQgY2FuIHJlZ2lzdGVyIGNvbnRyYWN0cwooZGVmaW5lLWRhdGEtdmFyIGFkbWluIHByaW5jaXBhbCB0eC1zZW5kZXIpCgo7OyBNYXAgb2YgYWxsb3dlZCBjb250cmFjdHMgZm9yIGFzc2V0IHRyYW5zZmVycyAtIG1hcHMgTDEgY29udHJhY3QgcHJpbmNpcGFsIHRvIEwyIGNvbnRyYWN0IHByaW5jaXBhbAooZGVmaW5lLW1hcCBhbGxvd2VkLWNvbnRyYWN0cyBwcmluY2lwYWwgcHJpbmNpcGFsKQoKOzsgVXNlIHRyYWl0IGRlY2xhcmF0aW9ucwoodXNlLXRyYWl0IG5mdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQubmZ0LXRyYWl0Lm5mdC10cmFpdCkKKHVzZS10cmFpdCBmdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQuc2lwLTAxMC10cmFpdC1mdC1zdGFuZGFyZC5zaXAtMDEwLXRyYWl0KQoodXNlLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQgLnN1Ym5ldC10cmFpdHMtdjEubWludC1mcm9tLXN1Ym5ldC10cmFpdCkKCjs7IFVwZGF0ZSB0aGUgbWluZXIgZm9yIHRoaXMgY29udHJhY3QuCihkZWZpbmUtcHVibGljICh1cGRhdGUtbWluZXIgKG5ldy1taW5lciBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgKGFzc2VydHMhIChpcy1lcSB0eC1zZW5kZXIgKHZhci1nZXQgbWluZXIpKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKICAgICAgICAob2sgKHZhci1zZXQgbWluZXIgbmV3LW1pbmVyKSkKICAgICkKKQoKOzsgUmVnaXN0ZXIgYSBuZXcgRlQgY29udHJhY3QgdG8gYmUgc3VwcG9ydGVkIGJ5IHRoaXMgc3VibmV0LgooZGVmaW5lLXB1YmxpYyAocmVnaXN0ZXItbmV3LWZ0LWNvbnRyYWN0IChmdC1jb250cmFjdCA8ZnQtdHJhaXQ+KSAobDItY29udHJhY3QgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFZlcmlmeSB0aGF0IHR4LXNlbmRlciBpcyBhbiBhdXRob3JpemVkIGFkbWluCiAgICAgICAgKGFzc2VydHMhIChpcy1hZG1pbiB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBTZXQgdXAgdGhlIGFzc2V0cyB0aGF0IHRoZSBjb250cmFjdCBpcyBhbGxvd2VkIHRvIHRyYW5zZmVyCiAgICAgICAgKGFzc2VydHMhIChtYXAtaW5zZXJ0IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAiZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdDogKGNvbnRyYWN0LW9mIGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbDItY29udHJhY3Q6IGwyLWNvbnRyYWN0CiAgICAgICAgfSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IFJlZ2lzdGVyIGEgbmV3IE5GVCBjb250cmFjdCB0byBiZSBzdXBwb3J0ZWQgYnkgdGhpcyBzdWJuZXQuCihkZWZpbmUtcHVibGljIChyZWdpc3Rlci1uZXctbmZ0LWNvbnRyYWN0IChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pIChsMi1jb250cmFjdCBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgOzsgVmVyaWZ5IHRoYXQgdHgtc2VuZGVyIGlzIGFuIGF1dGhvcml6ZWQgYWRtaW4KICAgICAgICAoYXNzZXJ0cyEgKGlzLWFkbWluIHR4LXNlbmRlcikgKGVyciBFUlJfSU5WQUxJRF9NSU5FUikpCgogICAgICAgIDs7IFNldCB1cCB0aGUgYXNzZXRzIHRoYXQgdGhlIGNvbnRyYWN0IGlzIGFsbG93ZWQgdG8gdHJhbnNmZXIKICAgICAgICAoYXNzZXJ0cyEgKG1hcC1pbnNlcnQgYWxsb3dlZC1jb250cmFjdHMgKGNvbnRyYWN0LW9mIG5mdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAibmZ0IiwKICAgICAgICAgICAgbDEtY29udHJhY3Q6IChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpLAogICAgICAgICAgICBsMi1jb250cmFjdDogbDItY29udHJhY3QKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhIG1pbmVyCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGlzLW1pbmVyIChtaW5lci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIG1pbmVyLXRvLWNoZWNrICh2YXItZ2V0IG1pbmVyKSkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhbiBhZG1pbgo7OyBSZXR1cm5zIGJvb2wKKGRlZmluZS1wcml2YXRlIChpcy1hZG1pbiAoYWRkci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIGFkZHItdG8tY2hlY2sgKHZhci1nZXQgYWRtaW4pKQopCgo7OyBIZWxwZXIgZnVuY3Rpb246IGRldGVybWluZXMgd2hldGhlciB0aGUgY29tbWl0LWJsb2NrIG9wZXJhdGlvbiBzYXRpc2ZpZXMgcHJlLWNvbmRpdGlvbnMKOzsgbGlzdGVkIGluIGBjb21taXQtYmxvY2tgLgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChjYW4tY29tbWl0LWJsb2NrPyAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KSAgKHRhcmdldC1jaGFpbi10aXAgKGJ1ZmYgMzIpKSkKICAgIChiZWdpbgogICAgICAgIDs7IGNoZWNrIG5vIGJsb2NrIGhhcyBiZWVuIGNvbW1pdHRlZCBhdCB0aGlzIGhlaWdodAogICAgICAgIChhc3NlcnRzISAoaXMtbm9uZSAobWFwLWdldD8gYmxvY2stY29tbWl0cyBjb21taXQtYmxvY2staGVpZ2h0KSkgKGVyciBFUlJfQkxPQ0tfQUxSRUFEWV9DT01NSVRURUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IGB0YXJnZXQtY2hhaW4tdGlwYCBtYXRjaGVzIHRoZSBidXJuIGNoYWluIHRpcAogICAgICAgIChhc3NlcnRzISAoaXMtZXEKICAgICAgICAgICAgdGFyZ2V0LWNoYWluLXRpcAogICAgICAgICAgICAodW53cmFwISAoZ2V0LWJsb2NrLWluZm8/IGlkLWhlYWRlci1oYXNoICgtIGJsb2NrLWhlaWdodCB1MSkpIChlcnIgRVJSX0NBTExFRF9UT09fRUFSTFkpKSApCiAgICAgICAgICAgIChlcnIgRVJSX0lOVkFMSURfQ0hBSU5fVElQKSkKCiAgICAgICAgOzsgY2hlY2sgdGhhdCB0aGUgdHggc2VuZGVyIGlzIG9uZSBvZiB0aGUgbWluZXJzCiAgICAgICAgKGFzc2VydHMhIChpcy1taW5lciB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSBtaW5lciBjYWxsZWQgdGhpcyBjb250cmFjdCBkaXJlY3RseQogICAgICAgIChhc3NlcnRzISAoaXMtbWluZXIgY29udHJhY3QtY2FsbGVyKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEhlbHBlciBmdW5jdGlvbjogbW9kaWZpZXMgdGhlIGJsb2NrLWNvbW1pdHMgbWFwIHdpdGggYSBuZXcgY29tbWl0IGFuZCBwcmludHMgcmVsYXRlZCBpbmZvCjs7IFJldHVybnMgcmVzcG9uc2U8KGJ1ZmYgMzIpLCA/PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLWNvbW1pdC1ibG9jawogICAgICAgIChibG9jayAoYnVmZiAzMikpCiAgICAgICAgKGNvbW1pdC1ibG9jay1oZWlnaHQgdWludCkKICAgICAgICAodGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICApCiAgICAoYmVnaW4KICAgICAgICAobWFwLXNldCBibG9jay1jb21taXRzIGNvbW1pdC1ibG9jay1oZWlnaHQgYmxvY2spCiAgICAgICAgKG1hcC1zZXQgd2l0aGRyYXdhbC1yb290cy1tYXAgd2l0aGRyYXdhbC1yb290IHRydWUpCiAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgZXZlbnQ6ICJibG9jay1jb21taXQiLAogICAgICAgICAgICBibG9jay1jb21taXQ6IGJsb2NrLAogICAgICAgICAgICBibG9jay1oZWlnaHQ6IGNvbW1pdC1ibG9jay1oZWlnaHQsCiAgICAgICAgICAgIHdpdGhkcmF3YWwtcm9vdDogd2l0aGRyYXdhbC1yb290LAogICAgICAgICAgICB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQ6IHRhcmdldC1idXJuLWJsb2NrLWhlaWdodAogICAgICAgIH0pCiAgICAgICAgKG9rIGJsb2NrKQogICAgKQopCgo7OyBUaGUgc3VibmV0IG1pbmVyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gY29tbWl0IGEgYmxvY2sgYXQgYSBwYXJ0aWN1bGFyIGhlaWdodC4KOzsgYGJsb2NrYCBpcyB0aGUgaGFzaCBvZiB0aGUgYmxvY2sgYmVpbmcgc3VibWl0dGVkLgo7OyBgdGFyZ2V0LWNoYWluLXRpcGAgaXMgdGhlIGBpZC1oZWFkZXItaGFzaGAgb2YgdGhlIGJ1cm4gYmxvY2sgKGkuZS4sIGJsb2NrIG9uCjs7ICAgIHRoaXMgY2hhaW4pIHRoYXQgdGhlIG1pbmVyIGludGVuZHMgdG8gYnVpbGQgb2ZmLgo7Owo7OyBGYWlscyBpZjoKOzsgIDEpIHdlIGhhdmUgYWxyZWFkeSBjb21taXR0ZWQgYXQgdGhpcyBibG9jayBoZWlnaHQKOzsgIDIpIGB0YXJnZXQtY2hhaW4tdGlwYCBpcyBub3QgdGhlIGJ1cm4gY2hhaW4gdGlwIChpLmUuLCBvbiB0aGlzIGNoYWluKQo7OyAgMykgdGhlIHNlbmRlciBpcyBub3QgYSBtaW5lcgooZGVmaW5lLXB1YmxpYyAoY29tbWl0LWJsb2NrCiAgICAgICAgKGJsb2NrIChidWZmIDMyKSkKICAgICAgICAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KQogICAgICAgICh0YXJnZXQtY2hhaW4tdGlwIChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKCh0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgYmxvY2staGVpZ2h0KSkKICAgICAgICAodHJ5ISAoY2FuLWNvbW1pdC1ibG9jaz8gdGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHRhcmdldC1jaGFpbi10aXApKQogICAgICAgIChpbm5lci1jb21taXQtYmxvY2sgYmxvY2sgY29tbWl0LWJsb2NrLWhlaWdodCB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgd2l0aGRyYXdhbC1yb290KQogICAgKQopCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBORlQgQVNTRVQgVFJBTlNGRVJTCgo7OyBIZWxwZXIgZnVuY3Rpb24gdGhhdCB0cmFuc2ZlcnMgdGhlIHNwZWNpZmllZCBORlQgZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgdHJhbnNmZXIgaWQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChhcy1jb250cmFjdCAoY29udHJhY3QtY2FsbD8gbmZ0LW1pbnQtY29udHJhY3QgbWludC1mcm9tLXN1Ym5ldCBpZCBzZW5kZXIgcmVjaXBpZW50KSkpCiAgICAgICAgICAgIChtaW50LXJlc3VsdCAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICkKICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB0cmFuc2ZlciBzdWNjZWVkZWQKICAgICAgICAoYXNzZXJ0cyEgbWludC1yZXN1bHQgKGVyciBFUlJfTUlOVF9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikKICAgICAgICAoaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGNvbnRyYWN0LWNhbGw/IG5mdC1jb250cmFjdCBnZXQtb3duZXIgaWQpKQogICAgICAgICAgICAobmZ0LW93bmVyICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLW5mdCAoaXMtZXEgbmZ0LW93bmVyIChzb21lIENPTlRSQUNUX0FERFJFU1MpKSkKICAgICAgICAgICAgKG5vLW93bmVyIChpcy1lcSBuZnQtb3duZXIgbm9uZSkpCiAgICAgICAgKQoKICAgICAgICAoaWYgY29udHJhY3Qtb3ducy1uZnQKICAgICAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgIChpZiBuby1vd25lcgogICAgICAgICAgICAgICAgOzsgVHJ5IG1pbnRpbmcgdGhlIGFzc2V0IGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG93bmVyIG9mIHRoaXMgTkZUCiAgICAgICAgICAgICAgICAoaW5uZXItbWludC1uZnQtYXNzZXQgbmZ0LW1pbnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgICAgICA7OyBJbiB0aGlzIGNhc2UsIGEgcHJpbmNpcGFsIG90aGVyIHRoYW4gdGhpcyBjb250cmFjdCBvd25zIHRoaXMgTkZULCBzbyBtaW50aW5nIGlzIG5vdCBwb3NzaWJsZQogICAgICAgICAgICAgICAgKGVyciBFUlJfTUlOVF9GQUlMRUQpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgYW4gTkZUIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQoKICAgICAgICA7OyBUcnkgdG8gdHJhbnNmZXIgdGhlIE5GVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1uZnQtYXNzZXQgbmZ0LWNvbnRyYWN0IGlkIHNlbmRlciBDT05UUkFDVF9BRERSRVNTKSkgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgOzsgRW1pdCBhIHByaW50IGV2ZW50IC0gdGhlIG5vZGUgY29uc3VtZXMgdGhpcwogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1uZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IG5mdC1jb250cmFjdCksCiAgICAgICAgICAgIG5mdC1pZDogaWQsCiAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICBzdWJuZXQtY29udHJhY3QtaWQ6IHN1Ym5ldC1jb250cmFjdC1pZCwKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IEhlbHBlciBmdW5jdGlvbiBmb3IgYHdpdGhkcmF3LW5mdC1hc3NldGAKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChpbm5lci13aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChsMi1jb250cmFjdCBwcmluY2lwYWwpCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAobGV0ICgoaGFzaGVzLWFyZS12YWxpZCAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMgd2l0aGRyYXdhbC1yb290IHdpdGhkcmF3YWwtbGVhZi1oYXNoIHNpYmxpbmctaGFzaGVzKSkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISBoYXNoZXMtYXJlLXZhbGlkKSAoZXJyIEVSUl9WQUxJREFUSU9OX0ZBSUxFRCkpCgogICAgICAgIDs7IGNoZWNrIHRoYXQgdGhlIHdpdGhkcmF3YWwgcmVxdWVzdCBkYXRhIG1hdGNoZXMgdGhlIHN1cHBsaWVkIGxlYWYgaGFzaAogICAgICAgIChhc3NlcnRzISAoaXMtZXEgd2l0aGRyYXdhbC1sZWFmLWhhc2gKICAgICAgICAgICAgICAgICAgICAgICAgIChsZWFmLWhhc2gtd2l0aGRyYXctbmZ0IGwyLWNvbnRyYWN0IGlkIHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBuZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgICAgIG1pbnQtY29udHJhY3QgKGFzLWNvbnRyYWN0IChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgbWludC1jb250cmFjdCBpZCByZWNpcGllbnQpKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgcmVjaXBpZW50KSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKQogICAgICAgICkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIHdpdGhkcmF3IHRoZSBzcGVjaWZpZWQgTkZUIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChpZCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChuZnQtbWludC1jb250cmFjdCAob3B0aW9uYWwgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIGFzc2V0IGJlbG9uZ3MgdG8gdGhlIGFsbG93ZWQtY29udHJhY3RzIG1hcAogICAgICAgICAgICAobDItY29udHJhY3QgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgICAodHJ5ISAoaW5uZXItd2l0aGRyYXctbmZ0LWFzc2V0CiAgICAgICAgICAgICAgICBuZnQtY29udHJhY3QKICAgICAgICAgICAgICAgIGwyLWNvbnRyYWN0CiAgICAgICAgICAgICAgICBpZAogICAgICAgICAgICAgICAgcmVjaXBpZW50CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWlkCiAgICAgICAgICAgICAgICBoZWlnaHQKICAgICAgICAgICAgICAgIG5mdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLXJvb3QKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICBzaWJsaW5nLWhhc2hlcwogICAgICAgICAgICApKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgewogICAgICAgICAgICBldmVudDogIndpdGhkcmF3LW5mdCIsCiAgICAgICAgICAgIGwxLWNvbnRyYWN0LWlkOiAoYXMtY29udHJhY3QgbmZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbmZ0LWlkOiBpZCwKICAgICAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IExpa2UgYGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtbmZ0LWFzc2V0IGJ1dCB3aXRob3V0IGFsbG93aW5nIG9yIHJlcXVpcmluZyBhIG1pbnQgZnVuY3Rpb24uIEluIG9yZGVyIHRvIHdpdGhkcmF3LCB0aGUgdXNlciBtdXN0Cjs7IGhhdmUgdGhlIGFwcHJvcHJpYXRlIGJhbGFuY2UuCihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgZ2V0LW93bmVyIGlkKSkKICAgICAgICAgICAgKG5mdC1vd25lciAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICAoY29udHJhY3Qtb3ducy1uZnQgKGlzLWVxIG5mdC1vd25lciAoc29tZSBDT05UUkFDVF9BRERSRVNTKSkpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEgY29udHJhY3Qtb3ducy1uZnQgKGVyciBFUlJfTkZUX05PVF9PV05FRF9CWV9DT05UUkFDVCkpCiAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICApCikKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgRk9SIEZVTkdJQkxFIFRPS0VOIEFTU0VUIFRSQU5TRkVSUwoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aGUgZnVuZ2libGUgdG9rZW4gZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChzZW5kZXIgcHJpbmNpcGFsKQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgdHJhbnNmZXIgYW1vdW50IHNlbmRlciByZWNpcGllbnQgbWVtbykpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgRklYTUU6IFNJUC0wMTAgZG9lc24ndCByZXF1aXJlIHRoYXQgdHJhbnNmZXIgcmV0dXJucyAob2sgdHJ1ZSkgb24gc3VjY2Vzcywgc28gaXMgdGhpcyBjaGVjayBuZWNlc3Nhcnk/CiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LWZ0LWFzc2V0CiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAoc2VuZGVyIHByaW5jaXBhbCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGFzLWNvbnRyYWN0IChjb250cmFjdC1jYWxsPyBmdC1taW50LWNvbnRyYWN0IG1pbnQtZnJvbS1zdWJuZXQgYW1vdW50IHNlbmRlciByZWNpcGllbnQpKSkKICAgICAgICAgICAgKG1pbnQtcmVzdWx0ICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISBtaW50LXJlc3VsdCAoZXJyIEVSUl9NSU5UX0ZBSUxFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgZ2V0LWJhbGFuY2UgQ09OVFJBQ1RfQUREUkVTUykpCiAgICAgICAgICAgIChjb250cmFjdC1mdC1iYWxhbmNlICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLWVub3VnaCAoPj0gY29udHJhY3QtZnQtYmFsYW5jZSBhbW91bnQpKQogICAgICAgICAgICAoYW1vdW50LXRvLXRyYW5zZmVyIChpZiBjb250cmFjdC1vd25zLWVub3VnaCBhbW91bnQgY29udHJhY3QtZnQtYmFsYW5jZSkpCiAgICAgICAgICAgIChhbW91bnQtdG8tbWludCAoLSBhbW91bnQgYW1vdW50LXRvLXRyYW5zZmVyKSkKICAgICAgICApCgogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRvdGFsIGJhbGFuY2UgYmV0d2VlbiB0aGUgdHJhbnNmZXIgYW5kIG1pbnQgaXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIGJhbGFuY2UKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIGFtb3VudCAoKyBhbW91bnQtdG8tdHJhbnNmZXIgYW1vdW50LXRvLW1pbnQpKSAoZXJyIEVSUl9JTl9DT01QVVRBVElPTikpCgogICAgICAgIChhbmQKICAgICAgICAgICAgKD4gYW1vdW50LXRvLXRyYW5zZmVyIHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50LXRvLXRyYW5zZmVyIENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICkKICAgICAgICAoYW5kCiAgICAgICAgICAgICg+IGFtb3VudC10by1taW50IHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItbWludC1mdC1hc3NldCBmdC1taW50LWNvbnRyYWN0IGFtb3VudC10by1taW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50KSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FsbHMgdGhpcyBmdW5jdGlvbiB0byBkZXBvc2l0IGEgZnVuZ2libGUgdG9rZW4gaW50byB0aGUgY29udHJhY3QuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKGRlcG9zaXQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKSkKICAgICAgICApCiAgICAgICAgOzsgVHJ5IHRvIHRyYW5zZmVyIHRoZSBGVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1mdC1hc3NldCBmdC1jb250cmFjdCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MgbWVtbykpIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkpCgogICAgICAgIChsZXQgKAogICAgICAgICAgICAgICAgKGZ0LW5hbWUgKHVud3JhcCEgKGNvbnRyYWN0LWNhbGw/IGZ0LWNvbnRyYWN0IGdldC1uYW1lKSAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICApCiAgICAgICAgICAgIDs7IEVtaXQgYSBwcmludCBldmVudCAtIHRoZSBub2RlIGNvbnN1bWVzIHRoaXMKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1mdCIsCiAgICAgICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgICAgIGZ0LW5hbWU6IGZ0LW5hbWUsCiAgICAgICAgICAgICAgICBmdC1hbW91bnQ6IGFtb3VudCwKICAgICAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICAgICAgc3VibmV0LWNvbnRyYWN0LWlkOiBzdWJuZXQtY29udHJhY3QtaWQsCiAgICAgICAgICAgIH0pCiAgICAgICAgKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBwZXJmb3JtcyB2YWxpZGl0eSBjaGVja3MgcmVsYXRlZCB0byB0aGUgd2l0aGRyYXdhbCBhbmQgcGVyZm9ybXMgdGhlIHdpdGhkcmF3YWwgYXMgd2VsbC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItd2l0aGRyYXctZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgKG9wdGlvbmFsIDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgaGFzaGVzLWFyZS12YWxpZCkgKGVyciBFUlJfVkFMSURBVElPTl9GQUlMRUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LWZ0IChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgYW1vdW50IHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBmdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICAgICAgbWludC1jb250cmFjdCAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQgZnQtY29udHJhY3QgbWludC1jb250cmFjdCBhbW91bnQgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICAgICAgICAgIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkKICAgICAgICApCgogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgKGVyciBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB3aXRoZHJhdyBzb21lIGFtb3VudCBvZiBhIGZ1bmdpYmxlIHRva2VuIGFzc2V0IGZyb20gdGhlCjs7IGNvbnRyYWN0IGFuZCBzZW5kIGl0IHRvIGEgcmVjaXBpZW50Lgo7OyBJbiBvcmRlciBmb3IgdGhpcyB3aXRoZHJhd2FsIHRvIGdvIHRocm91Z2gsIHRoZSBnaXZlbiB3aXRoZHJhd2FsIG11c3QgaGF2ZSBiZWVuIGluY2x1ZGVkCjs7IGluIGEgd2l0aGRyYXdhbCBNZXJrbGUgdHJlZSBhIHN1Ym5ldCBtaW5lciBzdWJtaXR0ZWQuIFRoZSB1c2VyIG11c3QgcHJvdmlkZSB0aGUgbGVhZgo7OyBoYXNoIG9mIHRoZWlyIHdpdGhkcmF3YWwgYW5kIHRoZSByb290IGhhc2ggb2YgdGhlIHNwZWNpZmljIE1lcmtsZSB0cmVlIHRoZWlyIHdpdGhkcmF3YWwKOzsgaXMgaW5jbHVkZWQgaW4uIFRoZXkgbXVzdCBhbHNvIHByb3ZpZGUgYSBsaXN0IG9mIHNpYmxpbmcgaGFzaGVzLiBUaGUgd2l0aGRyYXcgZnVuY3Rpb24KOzsgdXNlcyB0aGUgcHJvdmlkZWQgaGFzaGVzIHRvIGVuc3VyZSB0aGUgcmVxdWVzdGVkIHdpdGhkcmF3YWwgaXMgdmFsaWQuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKHdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgICAgIChmdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAoYmVnaW4KICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB3aXRoZHJhdyBhbW91bnQgaXMgcG9zaXRpdmUKICAgICAgICAoYXNzZXJ0cyEgKD4gYW1vdW50IHUwKSAoZXJyIEVSUl9BVFRFTVBUX1RPX1RSQU5TRkVSX1pFUk9fQU1PVU5UKSkKCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEgKGlubmVyLXdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgICAgICAgICBmdC1jb250cmFjdAogICAgICAgICAgICAgICAgYW1vdW50CiAgICAgICAgICAgICAgICByZWNpcGllbnQKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtaWQKICAgICAgICAgICAgICAgIGhlaWdodAogICAgICAgICAgICAgICAgbWVtbwogICAgICAgICAgICAgICAgZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWxlYWYtaGFzaAogICAgICAgICAgICAgICAgc2libGluZy1oYXNoZXMpKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAobGV0ICgKICAgICAgICAgICAgICAgIChmdC1uYW1lICh1bndyYXAhIChjb250cmFjdC1jYWxsPyBmdC1jb250cmFjdCBnZXQtbmFtZSkgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICAgICAgKQogICAgICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAid2l0aGRyYXctZnQiLAogICAgICAgICAgICAgICAgbDEtY29udHJhY3QtaWQ6IChhcy1jb250cmFjdCBmdC1jb250cmFjdCksCiAgICAgICAgICAgICAgICBmdC1uYW1lOiBmdC1uYW1lLAogICAgICAgICAgICAgICAgZnQtYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgfSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgoKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBTVFggVFJBTlNGRVJTCgoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIHRoZSBnaXZlbiBhbW91bnQgZnJvbSB0aGUgc3BlY2lmaWVkIGZ1bmdpYmxlIHRva2VuIGZyb20gdGhlIGdpdmVuIHNlbmRlciB0byB0aGUgZ2l2ZW4gcmVjaXBpZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1zdHggKGFtb3VudCB1aW50KSAoc2VuZGVyIHByaW5jaXBhbCkgKHJlY2lwaWVudCBwcmluY2lwYWwpKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoc3R4LXRyYW5zZmVyPyBhbW91bnQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISB0cmFuc2Zlci1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgU1RYIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LXN0eCAoYW1vdW50IHVpbnQpIChzZW5kZXIgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFRyeSB0byB0cmFuc2ZlciB0aGUgU1RYIHRvIHRoaXMgY29udHJhY3QKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQgLSB0aGUgbm9kZSBjb25zdW1lcyB0aGlzCiAgICAgICAgKHByaW50IHsgZXZlbnQ6ICJkZXBvc2l0LXN0eCIsIHNlbmRlcjogc2VuZGVyLCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICApCiAgICAoc2hhNTEyLzI1NiAoY29uY2F0IDB4MDAgKHVud3JhcC1wYW5pYyAodG8tY29uc2Vuc3VzLWJ1ZmY/CiAgICAgICAgewogICAgICAgICAgICB0eXBlOiAic3R4IiwKICAgICAgICAgICAgYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1uZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChuZnQtaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJuZnQiLAogICAgICAgICAgICBuZnQtaWQ6IG5mdC1pZCwKICAgICAgICAgICAgYXNzZXQtY29udHJhY3Q6IGFzc2V0LWNvbnRyYWN0LAogICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgd2l0aGRyYXdhbC1pZDogd2l0aGRyYXdhbC1pZCwKICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQKICAgICAgICB9KSkpCiAgICApCikKCihkZWZpbmUtcmVhZC1vbmx5IChsZWFmLWhhc2gtd2l0aGRyYXctZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJmdCIsCiAgICAgICAgICAgIGFtb3VudDogYW1vdW50LAogICAgICAgICAgICBhc3NldC1jb250cmFjdDogYXNzZXQtY29udHJhY3QsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKOzsgQSB1c2VyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gd2l0aGRyYXcgU1RYIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKCiAgICAgICAgKGFzc2VydHMhICh0cnkhIGhhc2hlcy1hcmUtdmFsaWQpIChlcnIgRVJSX1ZBTElEQVRJT05fRkFJTEVEKSkKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LXN0eCBhbW91bnQgcmVjaXBpZW50IHdpdGhkcmF3YWwtaWQgaGVpZ2h0KSkKICAgICAgICAgICAgICAgICAgKGVyciBFUlJfVkFMSURBVElPTl9MRUFGX0ZBSUxFRCkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgdHgtc2VuZGVyIHJlY2lwaWVudCkpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgIChmaW5pc2gtd2l0aGRyYXcgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogd2l0aGRyYXdhbC1sZWFmLWhhc2gsIHdpdGhkcmF3YWwtcm9vdC1oYXNoOiB3aXRoZHJhd2FsLXJvb3QgfSkKICAgICAgICAgIChlcnIgRVJSX1dJVEhEUkFXQUxfQUxSRUFEWV9QUk9DRVNTRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgeyBldmVudDogIndpdGhkcmF3LXN0eCIsIHJlY2lwaWVudDogcmVjaXBpZW50LCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBHRU5FUkFMIFdJVEhEUkFXQUwgRlVOQ1RJT05TCgo7OyBUaGlzIGZ1bmN0aW9uIGNvbmNhdHMgdGhlIHR3byBnaXZlbiBoYXNoZXMgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuIEl0IGFsc28gcHJlcGVuZHMgdGhlIGJ1ZmYgYDB4MDFgLCB3aGljaCBpcwo7OyBhIHRhZyBkZW5vdGluZyBhIG5vZGUgKHZlcnN1cyBhIGxlYWYpLgo7OyBSZXR1cm5zIGEgYnVmZgooZGVmaW5lLXByaXZhdGUgKGNyZWF0ZS1ub2RlLWhhc2gKICAgICAgICAoY3Vyci1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoIChidWZmIDMyKSkKICAgICAgICAoaXMtc2libGluZy1sZWZ0LXNpZGUgYm9vbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY29uY2F0dGVkLWhhc2ggKGlmIGlzLXNpYmxpbmctbGVmdC1zaWRlCiAgICAgICAgICAgICAgICAgICAgKGNvbmNhdCBzaWJsaW5nLWhhc2ggY3Vyci1oYXNoKQogICAgICAgICAgICAgICAgICAgIChjb25jYXQgY3Vyci1oYXNoIHNpYmxpbmctaGFzaCkKICAgICAgICAgICAgICAgICkpCiAgICAgICAgICApCgogICAgICAgICAgKGNvbmNhdCAweDAxIGNvbmNhdHRlZC1oYXNoKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIGhhc2hlcyB0aGUgY3VyciBoYXNoIHdpdGggaXRzIHNpYmxpbmcgaGFzaC4KOzsgUmV0dXJucyAoYnVmZiAzMikKKGRlZmluZS1wcml2YXRlIChoYXNoLWhlbHAKICAgICAgICAoc2libGluZyB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pCiAgICAgICAgKGN1cnItbm9kZS1oYXNoIChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoc2libGluZy1oYXNoIChnZXQgaGFzaCBzaWJsaW5nKSkKICAgICAgICAgICAgKGlzLXNpYmxpbmctbGVmdC1zaWRlIChnZXQgaXMtbGVmdC1zaWRlIHNpYmxpbmcpKQogICAgICAgICAgICAobmV3LWJ1ZmYgKGNyZWF0ZS1ub2RlLWhhc2ggY3Vyci1ub2RlLWhhc2ggc2libGluZy1oYXNoIGlzLXNpYmxpbmctbGVmdC1zaWRlKSkKICAgICAgICApCiAgICAgICAoc2hhNTEyLzI1NiBuZXctYnVmZikKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBjaGVja3M6Cjs7ICAtIFRoYXQgdGhlIHByb3ZpZGVkIHdpdGhkcmF3YWwgcm9vdCBtYXRjaGVzIGEgcHJldmlvdXNseSBzdWJtaXR0ZWQgb25lIChwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uIGBjb21taXQtYmxvY2tgKQo7OyAgLSBUaGF0IHRoZSBjb21wdXRlZCB3aXRoZHJhd2FsIHJvb3QgbWF0Y2hlcyBhIHByZXZpb3VzIHZhbGlkIHdpdGhkcmF3YWwgcm9vdAo7OyAgLSBUaGF0IHRoZSBnaXZlbiB3aXRoZHJhd2FsIGxlYWYgaGFzaCBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBwcm9jZXNzZWQKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGJlZ2luCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdXNlciBzdWJtaXR0ZWQgYSB2YWxpZCB3aXRoZHJhd2FsIHJvb3QKICAgICAgICAoYXNzZXJ0cyEgKGlzLXNvbWUgKG1hcC1nZXQ/IHdpdGhkcmF3YWwtcm9vdHMtbWFwIHdpdGhkcmF3YWwtcm9vdCkpIChlcnIgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QpKQoKICAgICAgICA7OyBDaGVjayB0aGF0IHRoaXMgd2l0aGRyYXdhbCBsZWFmIGhhcyBub3QgYmVlbiBwcm9jZXNzZWQgYmVmb3JlCiAgICAgICAgKGFzc2VydHMhCiAgICAgICAgICAgIChpcy1ub25lCiAgICAgICAgICAgICAobWFwLWdldD8gcHJvY2Vzc2VkLXdpdGhkcmF3YWwtbGVhdmVzLW1hcAogICAgICAgICAgICAgICAgICAgICAgIHsgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IHdpdGhkcmF3YWwtbGVhZi1oYXNoLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogd2l0aGRyYXdhbC1yb290IH0pKQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKSkKCiAgICAgICAgKGxldCAoKGNhbGN1bGF0ZWQtd2l0aGRyYXdhbC1yb290IChmb2xkIGhhc2gtaGVscCBzaWJsaW5nLWhhc2hlcyB3aXRoZHJhd2FsLWxlYWYtaGFzaCkpCiAgICAgICAgICAgICAgKHJvb3RzLW1hdGNoIChpcy1lcSBjYWxjdWxhdGVkLXdpdGhkcmF3YWwtcm9vdCB3aXRoZHJhd2FsLXJvb3QpKSkKICAgICAgICAgICAgIChpZiByb290cy1tYXRjaAogICAgICAgICAgICAgICAgKG9rIHRydWUpCiAgICAgICAgICAgICAgICAoZXJyIEVSUl9NRVJLTEVfUk9PVF9ET0VTX05PVF9NQVRDSCkpCiAgICAgICAgKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBjYWxsZWQgYWZ0ZXIgdGhlIGFzc2V0IGluIHF1ZXN0aW9uIGhhcyBiZWVuIHRyYW5zZmVycmVkLgo7OyBJdCBhZGRzIHRoZSB3aXRoZHJhd2FsIGxlYWYgaGFzaCB0byBhIG1hcCBvZiBwcm9jZXNzZWQgbGVhdmVzLiBUaGlzIGVuc3VyZXMgdGhhdAo7OyB0aGlzIHdpdGhkcmF3YWwgbGVhZiBjYW4ndCBiZSB1c2VkIGFnYWluIHRvIHdpdGhkcmF3IGFkZGl0aW9uYWwgZnVuZHMuCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGZpbmlzaC13aXRoZHJhdwogICAgICAgICh3aXRoZHJhdy1pbmZvIHsKICAgICAgICAgICAgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290LWhhc2g6IChidWZmIDMyKQogICAgICAgIH0pCiAgICApCiAgICAobWFwLWluc2VydCBwcm9jZXNzZWQtd2l0aGRyYXdhbC1sZWF2ZXMtbWFwIHdpdGhkcmF3LWluZm8gdHJ1ZSkKKQo=", + "clarity_version": "Clarity2", + "cost": 290960, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2.clar" + } + } + }, + { + "ContractPublish": { + "contract_name": "px", + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "location": { + "path": "contracts/px.clar" + }, + "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", + "clarity_version": "Clarity2", "cost": 18060, - "path": "contracts/px.clar", - "anchor-block-only": true, - "clarity-version": 2 + "anchor_block_only": true } } ], "epoch": "2.1" + }, + { + "id": 3, + "transactions": [ + { + "ContractCall": { + "contract_id": { + "issuer": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "name": "px" + }, + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "method": "set-value-at", + "parameters": ["u0", "0xfffffa"], + "cost": 2240, + "anchor_block_only": false + } + }, + { + "StxTransfer": { + "expected_sender": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", + "recipient": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", + "mstx_amount": 200, + "memo": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "cost": 2240, + "anchor_block_only": true + } + } + ], + "epoch": null } ] + }, + "contracts": { + "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px": [ + "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", + { + "path": "contracts/px.clar" + } + ] + } + }, + "network_manifest": { + "network": { + "name": "devnet", + "stacks_node_rpc_address": null, + "bitcoin_node_rpc_address": null, + "deployment_fee_rate": 10, + "sats_per_bytes": 10 + }, + "accounts": { + "deployer": { + "label": "deployer", + "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "btc_address": "mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH", + "is_mainnet": false + }, + "faucet": { + "label": "faucet", + "mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6", + "btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", + "is_mainnet": false + }, + "wallet_1": { + "label": "wallet_1", + "mnemonic": "crazy vibrant runway diagram beach language above aerobic maze coral this gas mirror output vehicle cover usage ecology unfold room feel file rocket expire", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "STB8E0SMACY4A6DCCH4WE48YGX3P877407QW176V", + "btc_address": "mha4u7F3e93P9Xy1WQgVvGtYtynnJtT22x", + "is_mainnet": false + }, + "wallet_2": { + "label": "wallet_2", + "mnemonic": "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG", + "btc_address": "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG", + "is_mainnet": false + }, + "wallet_3": { + "label": "wallet_3", + "mnemonic": "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC", + "btc_address": "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7", + "is_mainnet": false + }, + "wallet_4": { + "label": "wallet_4", + "mnemonic": "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND", + "btc_address": "mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8", + "is_mainnet": false + }, + "wallet_5": { + "label": "wallet_5", + "mnemonic": "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", + "btc_address": "mweN5WVqadScHdA81aATSdcVr4B6dNokqx", + "is_mainnet": false + }, + "wallet_6": { + "label": "wallet_6", + "mnemonic": "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", + "btc_address": "mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt", + "is_mainnet": false + }, + "wallet_7": { + "label": "wallet_7", + "mnemonic": "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ", + "btc_address": "n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7", + "is_mainnet": false + }, + "wallet_8": { + "label": "wallet_8", + "mnemonic": "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP", + "btc_address": "n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw", + "is_mainnet": false + } + }, + "devnet": { + "name": "devnet", + "network_id": null, + "orchestrator_ingestion_port": 20445, + "orchestrator_control_port": 20446, + "bitcoin_node_p2p_port": 18444, + "bitcoin_node_rpc_port": 18443, + "bitcoin_node_username": "devnet", + "bitcoin_node_password": "devnet", + "stacks_node_p2p_port": 20444, + "stacks_node_rpc_port": 20443, + "stacks_node_wait_time_for_microblocks": 50, + "stacks_node_first_attempt_time_ms": 500, + "stacks_node_subsequent_attempt_time_ms": 1000, + "stacks_node_events_observers": [], + "stacks_node_env_vars": [], + "stacks_api_port": 3999, + "stacks_api_events_port": 3700, + "stacks_api_env_vars": [], + "stacks_explorer_port": 8000, + "stacks_explorer_env_vars": [], + "bitcoin_explorer_port": 8001, + "bitcoin_controller_block_time": 60000, + "bitcoin_controller_automining_disabled": false, + "miner_stx_address": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", + "miner_secret_key_hex": "3b68e410cc7f9b8bae76f2f2991b69ecd0627c95da22a904065dfb2a73d0585f01", + "miner_btc_address": "n3GRiDLKWuKLCw1DZmV75W1mE35qmW2tQm", + "miner_mnemonic": "fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce", + "miner_derivation_path": "m/44'/5757'/0'/0/0", + "miner_coinbase_recipient": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", + "faucet_stx_address": "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6", + "faucet_secret_key_hex": "de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801", + "faucet_btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", + "faucet_mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", + "faucet_derivation_path": "m/44'/5757'/0'/0/0", + "working_dir": "/Users/micaiahreid/work/stx-px/tmp", + "postgres_port": 5432, + "postgres_username": "postgres", + "postgres_password": "postgres", + "stacks_api_postgres_database": "stacks_api", + "subnet_api_postgres_database": "subnet_api", + "pox_stacking_orders": [ + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_1", + "slots": 2, + "btc_address": "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" + }, + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_2", + "slots": 1, + "btc_address": "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG" + }, + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_3", + "slots": 1, + "btc_address": "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7" + } + ], + "execute_script": [], + "bitcoin_node_image_url": "quay.io/hirosystems/bitcoind:devnet-v3", + "stacks_node_image_url": "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0", + "stacks_api_image_url": "hirosystems/stacks-blockchain-api:latest", + "stacks_explorer_image_url": "hirosystems/explorer:latest", + "postgres_image_url": "postgres:14", + "bitcoin_explorer_image_url": "quay.io/hirosystems/bitcoin-explorer:devnet", + "disable_bitcoin_explorer": false, + "disable_stacks_explorer": false, + "disable_stacks_api": false, + "bind_containers_volumes": false, + "enable_subnet_node": false, + "subnet_node_image_url": "hirosystems/stacks-subnets:0.8.1", + "subnet_leader_stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "subnet_leader_secret_key_hex": "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", + "subnet_leader_btc_address": "mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH", + "subnet_leader_mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", + "subnet_leader_derivation_path": "m/44'/5757'/0'/0/0", + "subnet_node_p2p_port": 30444, + "subnet_node_rpc_port": 30443, + "subnet_events_ingestion_port": 30445, + "subnet_node_events_observers": [], + "subnet_contract_id": "ST173JK7NZBA4BS05ZRATQH1K89YJMTGEH1Z5J52E.subnet-v3-0-1", + "remapped_subnet_contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet-v3-0-1", + "subnet_node_env_vars": [], + "subnet_api_image_url": "hirosystems/stacks-blockchain-api:latest", + "subnet_api_port": 13999, + "subnet_api_events_port": 13700, + "subnet_api_env_vars": [], + "disable_subnet_api": true, + "docker_host": "unix:///var/run/docker.sock", + "components_host": "127.0.0.1", + "epoch_2_0": 100, + "epoch_2_05": 100, + "epoch_2_1": 101, + "epoch_2_2": 103, + "epoch_2_3": 104, + "epoch_2_4": 105, + "pox_2_activation": 102, + "use_docker_gateway_routing": false, + "docker_platform": "linux/amd64" } }, - "contracts": [ - { + "project_manifest": { + "project": { "name": "px", - "source": "XG47OyB0aXRsZTogcHhcbjs7IHZlcnNpb246XG47OyBzdW1tYXJ5OlxuOzsgZGVzY3JpcHRpb246IEFsbG93cyB1c2VycyB0byBwYXkgdG8gdXBkYXRlIGRhdGEgaW4gYSBtYXRyaXguIFxuOzsgIEVhY2ggbWF0cml4IHZhbHVlIG11c3QgYmUgYSBoZXhhZGVjaW1hbCB2YWx1ZSBmcm9tIDB4MDAwMDAwIHRvIDB4ZmZmZmZmLCByZXByZXNlbnRpbmcgYSBjb2xvciB0byBiZSBkaXNwbGF5ZWQgb24gYSBncmlkIGluIGEgd2ViIHBhZ2UuIFxuOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuXG5cbjs7IHRyYWl0c1xuOztcblxuOzsgdG9rZW4gZGVmaW5pdGlvbnNcbjs7IFxuXG47OyBjb25zdGFudHNcbjs7XG4oZGVmaW5lLWNvbnN0YW50IE1BWF9MT0MgdTEwMClcbihkZWZpbmUtY29uc3RhbnQgTUFYX1ZBTCAweGZmZmZmZilcbihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMClcbihkZWZpbmUtY29uc3RhbnQgQUxMX0xPQ1MgKGxpc3QgdTAgdTEgdTIgdTMgdTQgdTUgdTYgdTcgdTggdTkgdTEwIHUxMSB1MTIgdTEzIHUxNCB1MTUgdTE2IHUxNyB1MTggdTE5IHUyMCB1MjEgdTIyIHUyMyB1MjQgdTI1IHUyNiB1MjcgdTI4IHUyOSB1MzAgdTMxIHUzMiB1MzMgdTM0IHUzNSB1MzYgdTM3IHUzOCB1MzkgdTQwIHU0MSB1NDIgdTQzIHU0NCB1NDUgdTQ2IHU0NyB1NDggdTQ5IHU1MCB1NTEgdTUyIHU1MyB1NTQgdTU1IHU1NiB1NTcgdTU4IHU1OSB1NjAgdTYxIHU2MiB1NjMgdTY0IHU2NSB1NjYgdTY3IHU2OCB1NjkgdTcwIHU3MSB1NzIgdTczIHU3NCB1NzUgdTc2IHU3NyB1NzggdTc5IHU4MCB1ODEgdTgyIHU4MyB1ODQgdTg1IHU4NiB1ODcgdTg4IHU4OSB1OTAgdTkxIHU5MiB1OTMgdTk0IHU5NSB1OTYgdTk3IHU5OCB1OTkpKVxuOzsgZGF0YSB2YXJzXG47O1xuXG47OyBkYXRhIG1hcHNcbjs7XG4oZGVmaW5lLW1hcCBwaXhlbHMgdWludCAoYnVmZiAzKSlcblxuOzsgcHVibGljIGZ1bmN0aW9uc1xuOztcbihkZWZpbmUtcHVibGljIChzZXQtdmFsdWUtYXQgKGxvYyB1aW50KSAodmFsdWUgKGJ1ZmYgMykpKSBcbiAgICAoYmVnaW4gXG4gICAgICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgICAgICAoZXJyIFwiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy5cIilcbiAgICAgICAgICAgIChpZiAoPiB2YWx1ZSBNQVhfVkFMKVxuICAgICAgICAgICAgICAgIChlcnIgXCJWYWx1ZSBtdXN0IGJlIGxlc3MgdGhhbiAweGZmZmZmZi5cIilcbiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTClcbiAgICAgICAgICAgICAgICAgICAgKGVyciBcIlZhbHVlIG11c3QgYmUgZ3JlYXRlciB0aGFuIDB4MDAwMDAwLlwiKVxuICAgICAgICAgICAgICAgICAgICAob2sgKG1hcC1zZXQgcGl4ZWxzIGxvYyB2YWx1ZSkpXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKVxuICAgICAgICApXG4gICAgKVxuKVxuOzsgcmVhZCBvbmx5IGZ1bmN0aW9uc1xuOztcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdldC12YWx1ZS1hdCAobG9jIHVpbnQpKVxuICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgIChlcnIgXCJPdXQgb2YgYm91bmRzLlwiKVxuICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSlcbiAgICApXG4pXG5cbihkZWZpbmUtcmVhZC1vbmx5IChnZXQtYWxsKSBcbiAgICAobWFwIGdldC12YWx1ZS1hdCBBTExfTE9DUylcbilcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdlbmVzaXMtdGltZSAoaGVpZ2h0IHVpbnQpKVxuICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpXG4pXG47OyBwcml2YXRlIGZ1bmN0aW9uc1xuOztcbg==", - "clarity_version": 2, - "epoch": 2.1, - "deployer": null + "description": "", + "authors": [], + "telemetry": true, + "cache_dir": "./.cache", + "requirements": [] + }, + "contracts": { + "px": { + "path": "contracts/px.clar", + "clarity_version": 2, + "epoch": 2.1 + }, + "test": { + "path": "contracts/test.clar", + "clarity_version": 2, + "epoch": 2.1 + } + }, + "repl": { + "analysis": { + "passes": ["check_checker"], + "check_checker": { + "strict": true, + "trusted_sender": false, + "trusted_caller": false, + "callee_filter": false + } + } + }, + "location": { + "path": "Clarinet.toml" } - ] + } } diff --git a/src/config.rs b/src/config.rs index 47a925b..e9a8287 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,58 +1,36 @@ -use base64::{engine::general_purpose, Engine as _}; -use clarinet_deployments::types::DeploymentSpecificationFile; -use clarinet_files::{ - DEFAULT_DERIVATION_PATH, - DEFAULT_EPOCH_2_0, - DEFAULT_EPOCH_2_05, - DEFAULT_EPOCH_2_1, - DEFAULT_FAUCET_MNEMONIC, - DEFAULT_STACKS_MINER_MNEMONIC, //DEFAULT_EPOCH_2_2 (TODO, add when clarinet_files is updated) -}; +use clarinet_deployments::types::{DeploymentSpecification, TransactionSpecification}; +use clarinet_files::{AccountConfig, DevnetConfig, FileLocation, NetworkManifest, ProjectManifest}; use hiro_system_kit::slog; use serde::{Deserialize, Serialize}; -use std::str::from_utf8; +use std::{collections::BTreeMap, path::PathBuf}; use crate::{ resources::service::{get_service_port, ServicePort, StacksDevnetService}, Context, DevNetError, }; +const PROJECT_ROOT: &str = "/etc/stacks-network/project"; +const CONTRACT_DIR: &str = "/etc/stacks-network/project/contracts"; #[derive(Serialize, Deserialize, Debug)] pub struct ValidatedStacksDevnetConfig { - pub user_config: StacksDevnetConfig, + pub namespace: String, + pub devnet_config: DevnetConfig, + pub accounts: BTreeMap, pub project_manifest_yaml_string: String, pub network_manifest_yaml_string: String, pub deployment_plan_yaml_string: String, pub contract_configmap_data: Vec<(String, String)>, + pub disable_stacks_api: bool, } #[derive(Serialize, Deserialize, Debug)] pub struct StacksDevnetConfig { pub namespace: String, - pub stacks_node_wait_time_for_microblocks: Option, - pub stacks_node_first_attempt_time_ms: Option, - pub stacks_node_subsequent_attempt_time_ms: Option, - pub bitcoin_node_username: String, - pub bitcoin_node_password: String, - pub miner_mnemonic: Option, - pub miner_derivation_path: Option, - pub miner_coinbase_recipient: Option, - faucet_mnemonic: Option, - faucet_derivation_path: Option, - bitcoin_controller_block_time: Option, - bitcoin_controller_automining_disabled: Option, + pub disable_stacks_api: bool, disable_bitcoin_explorer: Option, // todo: currently unused disable_stacks_explorer: Option, // todo: currently unused - pub disable_stacks_api: bool, - pub epoch_2_0: Option, - pub epoch_2_05: Option, - pub epoch_2_1: Option, - pub epoch_2_2: Option, - pub pox_2_activation: Option, - pub pox_2_unlock_height: Option, // todo (not currently used) - project_manifest: ProjectManifestConfig, - pub accounts: Vec, - deployment_plan: DeploymentSpecificationFile, - contracts: Vec, + deployment_plan: DeploymentSpecification, + network_manifest: NetworkManifest, + project_manifest: ProjectManifest, } impl StacksDevnetConfig { pub fn to_validated_config( @@ -63,234 +41,140 @@ impl StacksDevnetConfig { "failed to validate config for NAMESPACE: {}", self.namespace ); - let project_manifest_yaml_string = self.get_project_manifest_yaml_string(); - let network_manifest_yaml_string = self.get_network_manifest_yaml_string(); - let deployment_plan_yaml_string = match self.get_deployment_plan_yaml_string() { - Ok(s) => Ok(s), - Err(e) => { - let msg = format!("{context}, ERROR: {e}"); - ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); - Err(DevNetError { - message: msg.into(), - code: 400, - }) - } - }?; + let project_manifest_yaml_string = self + .get_project_manifest_yaml_string() + .map_err(|e| log_and_return_err(e, &context, &ctx))?; + + let (network_manifest_yaml_string, devnet_config) = self + .get_network_manifest_string_and_devnet_config() + .map_err(|e| log_and_return_err(e, &context, &ctx))?; + + let deployment_plan_yaml_string = self + .get_deployment_plan_yaml_string() + .map_err(|e| log_and_return_err(e, &context, &ctx))?; let mut contracts: Vec<(String, String)> = vec![]; - for contract in &self.contracts { - let data = match contract.to_configmap_data() { - Ok(d) => Ok(d), - Err(e) => { - let msg = format!("{context}, ERROR: {e}"); - ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); - Err(DevNetError { - message: msg.into(), - code: 400, - }) - } - }?; - contracts.push(data); + for (contract_identifier, (src, _)) in self.deployment_plan.contracts { + contracts.push((contract_identifier.name.to_string(), src)); } + Ok(ValidatedStacksDevnetConfig { - user_config: self, - project_manifest_yaml_string, + namespace: self.namespace, + devnet_config: devnet_config.to_owned(), + accounts: self.network_manifest.accounts, + project_manifest_yaml_string: project_manifest_yaml_string.to_owned(), network_manifest_yaml_string, deployment_plan_yaml_string, contract_configmap_data: contracts, + disable_stacks_api: self.disable_stacks_api, }) } - fn get_project_manifest_yaml_string(&self) -> String { - self.project_manifest.to_yaml_string(&self) - } - - fn get_network_manifest_yaml_string(&self) -> String { - let mut config = format!( - r#"[network] -name = 'devnet' -"#, - ); + fn get_network_manifest_string_and_devnet_config( + &self, + ) -> Result<(String, DevnetConfig), String> { + let network_config = &self.network_manifest; - config.push_str( - &self - .accounts - .clone() - .iter() - .map(|a| a.to_yaml_string()) - .collect::>() - .join("\n"), - ); - config.push_str(&format!( - r#" -[devnet] -miner_mnemonic = "{}" -miner_derivation_path = "{}" -bitcoin_node_username = "{}" -bitcoin_node_password = "{}" -faucet_mnemonic = "{}" -faucet_derivation_path = "{}" -orchestrator_ingestion_port = {} -orchestrator_control_port = {} -bitcoin_node_rpc_port = {} -stacks_node_rpc_port = {} -stacks_api_port = {} -epoch_2_0 = {} -epoch_2_05 = {} -epoch_2_1 = {} -epoch_2_2 = {} -working_dir = "/devnet" -bitcoin_controller_block_time = {} -bitcoin_controller_automining_disabled = {}"#, - &self - .miner_mnemonic - .clone() - .unwrap_or(DEFAULT_STACKS_MINER_MNEMONIC.into()), - &self - .miner_derivation_path - .clone() - .unwrap_or(DEFAULT_DERIVATION_PATH.into()), - &self.bitcoin_node_username, - &self.bitcoin_node_password, - &self - .faucet_mnemonic - .clone() - .unwrap_or(DEFAULT_FAUCET_MNEMONIC.into()), - &self - .faucet_derivation_path - .clone() - .unwrap_or(DEFAULT_DERIVATION_PATH.into()), - get_service_port(StacksDevnetService::BitcoindNode, ServicePort::Ingestion).unwrap(), - get_service_port(StacksDevnetService::BitcoindNode, ServicePort::Control).unwrap(), - get_service_port(StacksDevnetService::BitcoindNode, ServicePort::RPC).unwrap(), - get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC).unwrap(), - get_service_port(StacksDevnetService::StacksApi, ServicePort::API).unwrap(), - &self.epoch_2_0.unwrap_or(DEFAULT_EPOCH_2_0), - &self.epoch_2_05.unwrap_or(DEFAULT_EPOCH_2_05), - &self.epoch_2_1.unwrap_or(DEFAULT_EPOCH_2_1), - &self.epoch_2_2.unwrap_or(122), // todo: should be DEFAULT_EPOCH_2_2 when clarinet_files is updated - &self.bitcoin_controller_block_time.unwrap_or(50), - &self.bitcoin_controller_automining_disabled.unwrap_or(false) - )); + let devnet_config = match &self.network_manifest.devnet { + Some(devnet_config) => Ok(devnet_config), + None => Err("network manifest is missing required devnet config"), + }?; + let mut devnet_config = devnet_config.clone(); + devnet_config.orchestrator_ingestion_port = + get_service_port(StacksDevnetService::BitcoindNode, ServicePort::Ingestion) + .unwrap() + .parse::() + .unwrap(); + devnet_config.orchestrator_control_port = + get_service_port(StacksDevnetService::BitcoindNode, ServicePort::Control) + .unwrap() + .parse::() + .unwrap(); + devnet_config.bitcoin_node_p2p_port = + get_service_port(StacksDevnetService::BitcoindNode, ServicePort::P2P) + .unwrap() + .parse::() + .unwrap(); + devnet_config.bitcoin_node_rpc_port = + get_service_port(StacksDevnetService::BitcoindNode, ServicePort::RPC) + .unwrap() + .parse::() + .unwrap(); + devnet_config.stacks_node_p2p_port = + get_service_port(StacksDevnetService::StacksNode, ServicePort::P2P) + .unwrap() + .parse::() + .unwrap(); + devnet_config.stacks_node_rpc_port = + get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC) + .unwrap() + .parse::() + .unwrap(); + devnet_config.stacks_api_port = + get_service_port(StacksDevnetService::StacksApi, ServicePort::API) + .unwrap() + .parse::() + .unwrap(); + devnet_config.stacks_api_events_port = + get_service_port(StacksDevnetService::StacksApi, ServicePort::Event) + .unwrap() + .parse::() + .unwrap(); + devnet_config.postgres_port = + get_service_port(StacksDevnetService::StacksApi, ServicePort::DB) + .unwrap() + .parse::() + .unwrap(); - config - } + let yaml_str = match serde_yaml::to_string(&network_config) { + Ok(s) => Ok(s), + Err(e) => Err(format!("failed to parse devnet config: {}", e)), + }?; - pub fn get_deployment_plan_yaml_string(&self) -> Result { - serde_yaml::to_string(&self.deployment_plan) - .map_err(|e| format!("failed to parse deployment plan config: {}", e)) + Ok((yaml_str, devnet_config)) } -} -#[derive(Serialize, Deserialize, Debug, Clone)] -struct ProjectManifestConfig { - name: String, - description: Option, - authors: Option>, - requirements: Option>, -} - -impl ProjectManifestConfig { - fn to_yaml_string(&self, config: &StacksDevnetConfig) -> String { - let description = match &self.description { - Some(d) => d.to_owned(), - None => String::new(), - }; - let authors = match &self.authors { - Some(a) => format!("['{}']", a.join("','")), - None => String::from("[]"), - }; - let requirements = match &self.requirements { - Some(r) => format!("['{}']", r.join("','")), - None => String::from("[]"), - }; - format!( - r#"[project] -name = "{}" -description = "{}" -authors = {} -requirements = {} - -{}"#, - &self.name, - description, - authors, - requirements, - &config - .contracts - .clone() - .into_iter() - .map(|c| c.to_project_manifest_yaml_string()) - .collect::>() - .join("\n") - ) + fn get_project_manifest_yaml_string(&self) -> Result { + let mut project_manifest = self.project_manifest.clone(); + project_manifest.location = FileLocation::from_path(PathBuf::from(PROJECT_ROOT)); + project_manifest.project.cache_location = + FileLocation::from_path(PathBuf::from(CONTRACT_DIR)); + serde_yaml::to_string(&project_manifest) + .map_err(|e| format!("failed to parse project manifest: {}", e)) } -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ContractConfig { - pub name: String, - pub source: String, - clarity_version: u32, - epoch: f64, - deployer: Option, -} -impl ContractConfig { - fn to_project_manifest_yaml_string(&self) -> String { - let mut config = format!( - r#"[contracts.{}] -path = "contracts/{}.clar" -clarity_version = {} -epoch = "{}""#, - &self.name, &self.name, self.clarity_version, self.epoch, - ); - if let Some(deployer) = &self.deployer { - config.push_str(&format!(r#"deployer = {}"#, deployer,)); + pub fn get_deployment_plan_yaml_string(&self) -> Result { + let deployment = self.deployment_plan.clone(); + let contracts_loc = FileLocation::from_path(PathBuf::from(CONTRACT_DIR)); + for b in deployment.plan.batches { + for t in b.transactions { + match t { + TransactionSpecification::ContractPublish(mut spec) => { + spec.location = contracts_loc.clone(); + } + TransactionSpecification::RequirementPublish(mut spec) => { + spec.location = contracts_loc.clone(); + }, + TransactionSpecification::EmulatedContractCall(_) | TransactionSpecification::EmulatedContractPublish(_) => { + return Err(format!("devnet deployment plans do not support emulated-contract-calls or emulated-contract-publish types")) + } + _ => {} + } + } } - config - } - - fn to_configmap_data(&self) -> Result<(String, String), String> { - let bytes = general_purpose::STANDARD - .decode(&self.source) - .map_err(|e| format!("unable to decode contract source: {}", e.to_string()))?; - - let decoded = from_utf8(&bytes).map_err(|e| { - format!( - "invalid UTF-8 sequence when decoding contract source: {}", - e.to_string() - ) - })?; - let filename = format!("{}.clar", &self.name); - Ok((filename, decoded.to_owned())) + serde_yaml::to_string(&self.deployment_plan) + .map_err(|e| format!("failed to parse deployment plan config: {}", e)) } } -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AccountConfig { - pub name: String, - pub mnemonic: String, - pub derivation: Option, - pub balance: u64, -} -impl AccountConfig { - pub fn to_yaml_string(&self) -> String { - let mut config = format!( - r#" -[accounts.{}] -mnemonic = "{}" -balance = "{}" -"#, - &self.name, &self.mnemonic, &self.balance - ); - if let Some(derivation) = &self.derivation { - config.push_str(&format!(r#"derivation = "{}""#, derivation)); - } - config +fn log_and_return_err(e: String, context: &str, ctx: &Context) -> DevNetError { + let msg = format!("{context}, ERROR: {e}"); + ctx.try_log(|logger: &hiro_system_kit::Logger| slog::warn!(logger, "{}", msg)); + DevNetError { + message: msg.into(), + code: 400, } } - #[cfg(test)] mod tests { use std::{ @@ -301,7 +185,7 @@ mod tests { use crate::Context; - use super::{ProjectManifestConfig, StacksDevnetConfig}; + use super::StacksDevnetConfig; fn read_file(file_path: &str) -> Vec { let file = File::open(file_path) @@ -325,18 +209,6 @@ mod tests { }; config_file } - #[test] - #[should_panic] - fn it_rejects_config_with_none_base64_source_code() { - let mut template = get_template_config("src/tests/fixtures/stacks-devnet-config.json"); - let logger = hiro_system_kit::log::setup_logger(); - let _guard = hiro_system_kit::log::setup_global_logger(logger.clone()); - let ctx = Context::empty(); - template.contracts[0].source = "invalid base64 string".to_string(); - template - .to_validated_config(ctx) - .unwrap_or_else(|e| panic!("config validation test failed: {}", e.message)); - } #[test] fn it_converts_config_to_yaml() { @@ -358,11 +230,7 @@ mod tests { let expected_deployment_plan = from_utf8(&expected_deployment_plan).unwrap(); let expected_contract_source = read_file("src/tests/fixtures/contract-source.clar"); - let escaped = expected_contract_source - .iter() - .flat_map(|b| std::ascii::escape_default(*b)) - .collect::>(); - let expected_contract_source = from_utf8(&escaped).unwrap(); + let expected_contract_source = from_utf8(&expected_contract_source).unwrap(); assert_eq!( expected_project_mainfest, @@ -383,23 +251,18 @@ mod tests { } #[test] - fn project_manifest_allows_omitted_values() { - let project_manifest = ProjectManifestConfig { - name: "Test".to_string(), - description: None, - authors: None, - requirements: None, - }; + #[should_panic] + fn it_requires_devnet_config() { let mut template = get_template_config("src/tests/fixtures/stacks-devnet-config.json"); - template.contracts = vec![]; - let yaml = project_manifest.to_yaml_string(&template); - let expected = r#"[project] -name = "Test" -description = "" -authors = [] -requirements = [] - -"#; - assert_eq!(expected.to_string(), yaml); + let logger = hiro_system_kit::log::setup_logger(); + let _guard = hiro_system_kit::log::setup_global_logger(logger.clone()); + let ctx = Context { + logger: None, + tracer: false, + }; + template.network_manifest.devnet = None; + template + .to_validated_config(ctx) + .unwrap_or_else(|e| panic!("config validation test failed: {}", e.message)); } } diff --git a/src/lib.rs b/src/lib.rs index 02fe87e..fb52b77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ use chainhook_types::StacksNetwork; -use clarinet_files::{ - compute_addresses, DEFAULT_DERIVATION_PATH, DEFAULT_EPOCH_2_0, DEFAULT_EPOCH_2_05, - DEFAULT_EPOCH_2_1, DEFAULT_POX2_ACTIVATION, DEFAULT_STACKS_MINER_MNEMONIC, -}; +use clarinet_files::compute_addresses; use futures::future::try_join4; use hiro_system_kit::{slog, Logger}; use hyper::{body::Bytes, Body, Client as HttpClient, Request, Response, Uri}; @@ -26,7 +23,7 @@ use strum::IntoEnumIterator; use tower::BoxError; pub mod config; -use config::{StacksDevnetConfig, ValidatedStacksDevnetConfig}; +use config::ValidatedStacksDevnetConfig; mod template_parser; use template_parser::get_yaml_from_resource; @@ -135,8 +132,7 @@ impl StacksDevnetApiK8sManager { &self, config: ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { - let user_config = config.user_config; - let namespace = &user_config.namespace; + let namespace = &config.namespace; let context = format!("NAMESPACE: {}", &namespace); let namespace_exists = self.check_namespace_exists(&namespace).await?; @@ -169,20 +165,13 @@ impl StacksDevnetApiK8sManager { }); }; - self.deploy_bitcoin_node_pod( - &user_config, - config.project_manifest_yaml_string, - config.network_manifest_yaml_string, - config.deployment_plan_yaml_string, - config.contract_configmap_data, - ) - .await?; + self.deploy_bitcoin_node_pod(&config).await?; sleep(Duration::from_secs(5)); - self.deploy_stacks_node_pod(&user_config).await?; + self.deploy_stacks_node_pod(&config).await?; - if !user_config.disable_stacks_api { + if !config.disable_stacks_api { self.deploy_stacks_api_pod(&namespace).await?; } Ok(()) @@ -794,13 +783,10 @@ impl StacksDevnetApiK8sManager { async fn deploy_bitcoin_node_pod( &self, - config: &StacksDevnetConfig, - project_mainfest: String, - network_manifest: String, - deployment_plan: String, - contracts: Vec<(String, String)>, + config: &ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let devnet_config = &config.devnet_config; let bitcoin_rpc_port = get_service_port(StacksDevnetService::BitcoindNode, ServicePort::RPC).unwrap(); @@ -831,8 +817,8 @@ impl StacksDevnetApiK8sManager { rpcbind=0.0.0.0:{} rpcport={} "#, - config.bitcoin_node_username, - config.bitcoin_node_password, + devnet_config.bitcoin_node_username, + devnet_config.bitcoin_node_password, bitcoin_p2p_port, bitcoin_rpc_port, bitcoin_rpc_port @@ -855,28 +841,37 @@ impl StacksDevnetApiK8sManager { self.deploy_configmap( StacksDevnetConfigmap::ProjectManifest, &namespace, - Some(vec![("Clarinet.toml".into(), project_mainfest)]), + Some(vec![( + "Clarinet.toml".into(), + config.project_manifest_yaml_string.to_owned(), + )]), ) .await?; self.deploy_configmap( StacksDevnetConfigmap::Devnet, &namespace, - Some(vec![("Devnet.toml".into(), network_manifest)]), + Some(vec![( + "Devnet.toml".into(), + config.network_manifest_yaml_string.to_owned(), + )]), ) .await?; self.deploy_configmap( StacksDevnetConfigmap::DeploymentPlan, &namespace, - Some(vec![("default.devnet-plan.yaml".into(), deployment_plan)]), + Some(vec![( + "default.devnet-plan.yaml".into(), + config.deployment_plan_yaml_string.to_owned(), + )]), ) .await?; self.deploy_configmap( StacksDevnetConfigmap::ProjectDir, &namespace, - Some(contracts), + Some(config.contract_configmap_data.to_owned()), ) .await?; @@ -889,21 +884,19 @@ impl StacksDevnetApiK8sManager { Ok(()) } - async fn deploy_stacks_node_pod(&self, config: &StacksDevnetConfig) -> Result<(), DevNetError> { + async fn deploy_stacks_node_pod( + &self, + config: &ValidatedStacksDevnetConfig, + ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let devnet_config = &config.devnet_config; let chain_coordinator_ingestion_port = get_service_port(StacksDevnetService::BitcoindNode, ServicePort::Ingestion).unwrap(); let (miner_coinbase_recipient, _, stacks_miner_secret_key_hex) = compute_addresses( - &config - .miner_mnemonic - .clone() - .unwrap_or(DEFAULT_STACKS_MINER_MNEMONIC.into()), - &config - .miner_derivation_path - .clone() - .unwrap_or(DEFAULT_DERIVATION_PATH.into()), + &devnet_config.miner_mnemonic, + &devnet_config.miner_derivation_path, &StacksNetwork::Devnet.get_networks(), ); @@ -941,31 +934,20 @@ impl StacksDevnetApiK8sManager { get_service_port(StacksDevnetService::StacksNode, ServicePort::P2P).unwrap(), stacks_miner_secret_key_hex, stacks_miner_secret_key_hex, - config.stacks_node_wait_time_for_microblocks.unwrap_or(50), - config.stacks_node_first_attempt_time_ms.unwrap_or(500), - config - .stacks_node_subsequent_attempt_time_ms - .unwrap_or(1_000), + devnet_config.stacks_node_wait_time_for_microblocks, + devnet_config.stacks_node_first_attempt_time_ms, + devnet_config.stacks_node_subsequent_attempt_time_ms, miner_coinbase_recipient ); - for account in config.accounts.clone().iter() { - let derivation_path = account - .derivation - .clone() - .unwrap_or(DEFAULT_DERIVATION_PATH.into()); - let (stx_address, _, _) = compute_addresses( - &account.mnemonic, - &derivation_path, - &StacksNetwork::Devnet.get_networks(), - ); + for (_, account) in config.accounts.iter() { stacks_conf.push_str(&format!( r#" [[ustx_balance]] address = "{}" amount = {} "#, - stx_address, account.balance + account.stx_address, account.balance )); } @@ -1023,8 +1005,8 @@ impl StacksDevnetApiK8sManager { peer_port = {} "#, bitcoind_chain_coordinator_host, - config.bitcoin_node_username, - config.bitcoin_node_password, + devnet_config.bitcoin_node_username, + devnet_config.bitcoin_node_password, chain_coordinator_ingestion_port, get_service_port(StacksDevnetService::BitcoindNode, ServicePort::P2P).unwrap() )); @@ -1053,11 +1035,11 @@ impl StacksDevnetApiK8sManager { # epoch_name = "2.2" # start_height = {} "#, - config.pox_2_activation.unwrap_or(DEFAULT_POX2_ACTIVATION), - config.epoch_2_0.unwrap_or(DEFAULT_EPOCH_2_0), - config.epoch_2_05.unwrap_or(DEFAULT_EPOCH_2_05), - config.epoch_2_1.unwrap_or(DEFAULT_EPOCH_2_1), - config.epoch_2_2.unwrap_or(110) //todo - get default value and uncomment config once stacks image is updated + devnet_config.pox_2_activation, + devnet_config.epoch_2_0, + devnet_config.epoch_2_05, + devnet_config.epoch_2_1, + devnet_config.epoch_2_2, )); stacks_conf }; diff --git a/src/tests/fixtures/deployment-plan.yaml b/src/tests/fixtures/deployment-plan.yaml index 267c4ad..21e25da 100644 --- a/src/tests/fixtures/deployment-plan.yaml +++ b/src/tests/fixtures/deployment-plan.yaml @@ -2,17 +2,88 @@ id: 0 name: Devnet deployment network: devnet -stacks-node: "http://localhost:20443" -bitcoin-node: "http://px-devnet:px-devnet@localhost:18443" -plan: - batches: - - id: 0 - transactions: - - contract-publish: - contract-name: px - expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM - cost: 18060 - path: contracts/px.clar - anchor-block-only: true - clarity-version: 2 - epoch: "2.1" +stacks_node: "http://localhost:20443" +bitcoin_node: "http://px-devnet:px-devnet@localhost:18443" +genesis: ~ +batches: + - id: 0 + transactions: + - transaction_type: RequirementPublish + contract_id: ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait + remap_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + remap_principals: + ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + source: KGRlZmluZS10cmFpdCBuZnQtdHJhaXQKICAoCiAgICA7OyBMYXN0IHRva2VuIElELCBsaW1pdGVkIHRvIHVpbnQgcmFuZ2UKICAgIChnZXQtbGFzdC10b2tlbi1pZCAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyBVUkkgZm9yIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgdG9rZW4KICAgIChnZXQtdG9rZW4tdXJpICh1aW50KSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctYXNjaWkgMjU2KSkgdWludCkpCgogICAgIDs7IE93bmVyIG9mIGEgZ2l2ZW4gdG9rZW4gaWRlbnRpZmllcgogICAgKGdldC1vd25lciAodWludCkgKHJlc3BvbnNlIChvcHRpb25hbCBwcmluY2lwYWwpIHVpbnQpKQoKICAgIDs7IFRyYW5zZmVyIGZyb20gdGhlIHNlbmRlciB0byBhIG5ldyBwcmluY2lwYWwKICAgICh0cmFuc2ZlciAodWludCBwcmluY2lwYWwgcHJpbmNpcGFsKSAocmVzcG9uc2UgYm9vbCB1aW50KSkKICApCik= + clarity_version: 1 + cost: 4670 + location: + path: "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.clar" + epoch: "2.0" + - id: 1 + transactions: + - transaction_type: RequirementPublish + contract_id: ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard + remap_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + remap_principals: + ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + source: KGRlZmluZS10cmFpdCBzaXAtMDEwLXRyYWl0CiAgKAogICAgOzsgVHJhbnNmZXIgZnJvbSB0aGUgY2FsbGVyIHRvIGEgbmV3IHByaW5jaXBhbAogICAgKHRyYW5zZmVyICh1aW50IHByaW5jaXBhbCBwcmluY2lwYWwgKG9wdGlvbmFsIChidWZmIDM0KSkpIChyZXNwb25zZSBib29sIHVpbnQpKQoKICAgIDs7IHRoZSBodW1hbiByZWFkYWJsZSBuYW1lIG9mIHRoZSB0b2tlbgogICAgKGdldC1uYW1lICgpIChyZXNwb25zZSAoc3RyaW5nLWFzY2lpIDMyKSB1aW50KSkKCiAgICA7OyB0aGUgdGlja2VyIHN5bWJvbCwgb3IgZW1wdHkgaWYgbm9uZQogICAgKGdldC1zeW1ib2wgKCkgKHJlc3BvbnNlIChzdHJpbmctYXNjaWkgMzIpIHVpbnQpKQoKICAgIDs7IHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdXNlZCwgZS5nLiA2IHdvdWxkIG1lYW4gMV8wMDBfMDAwIHJlcHJlc2VudHMgMSB0b2tlbgogICAgKGdldC1kZWNpbWFscyAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyB0aGUgYmFsYW5jZSBvZiB0aGUgcGFzc2VkIHByaW5jaXBhbAogICAgKGdldC1iYWxhbmNlIChwcmluY2lwYWwpIChyZXNwb25zZSB1aW50IHVpbnQpKQoKICAgIDs7IHRoZSBjdXJyZW50IHRvdGFsIHN1cHBseSAod2hpY2ggZG9lcyBub3QgbmVlZCB0byBiZSBhIGNvbnN0YW50KQogICAgKGdldC10b3RhbC1zdXBwbHkgKCkgKHJlc3BvbnNlIHVpbnQgdWludCkpCgogICAgOzsgYW4gb3B0aW9uYWwgVVJJIHRoYXQgcmVwcmVzZW50cyBtZXRhZGF0YSBvZiB0aGlzIHRva2VuCiAgICAoZ2V0LXRva2VuLXVyaSAoKSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctdXRmOCAyNTYpKSB1aW50KSkKICApCik= + clarity_version: 1 + cost: 8390 + location: + path: "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.clar" + epoch: "2.05" + - id: 2 + transactions: + - transaction_type: RequirementPublish + contract_id: ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1 + remap_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + remap_principals: + ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + source: OzsgSW4gb3JkZXIgdG8gc3VwcG9ydCB3aXRoZHJhd2luZyBhbiBhc3NldCB0aGF0IHdhcyBtaW50ZWQgb24gYSBzdWJuZXQsIHRoZQo7OyBMMSBjb250cmFjdCBtdXN0IGltcGxlbWVudCB0aGlzIHRyYWl0LgooZGVmaW5lLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQKICAoCiAgICA7OyBQcm9jZXNzIGEgd2l0aGRyYXdhbCBmcm9tIHRoZSBzdWJuZXQgZm9yIGFuIGFzc2V0IHdoaWNoIGRvZXMgbm90IHlldAogICAgOzsgZXhpc3Qgb24gdGhpcyBuZXR3b3JrLCBhbmQgdGh1cyByZXF1aXJlcyBhIG1pbnQuCiAgICAobWludC1mcm9tLXN1Ym5ldAogICAgICAoCiAgICAgICAgdWludCAgICAgICA7OyBhc3NldC1pZCAoTkZUKSBvciBhbW91bnQgKEZUKQogICAgICAgIHByaW5jaXBhbCAgOzsgc2VuZGVyCiAgICAgICAgcHJpbmNpcGFsICA7OyByZWNpcGllbnQKICAgICAgKQogICAgICAocmVzcG9uc2UgYm9vbCB1aW50KQogICAgKQogICkKKQ== + clarity_version: 2 + cost: 4810 + location: + path: "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1.clar" + - transaction_type: RequirementPublish + contract_id: ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2 + remap_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + remap_principals: + ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + source: OzsgVGhlIC5zdWJuZXQgY29udHJhY3QKCihkZWZpbmUtY29uc3RhbnQgQ09OVFJBQ1RfQUREUkVTUyAoYXMtY29udHJhY3QgdHgtc2VuZGVyKSkKCjs7IEVycm9yIGNvZGVzCihkZWZpbmUtY29uc3RhbnQgRVJSX0JMT0NLX0FMUkVBRFlfQ09NTUlUVEVEIDEpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUlORVIgMikKKGRlZmluZS1jb25zdGFudCBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQgMykKKGRlZmluZS1jb25zdGFudCBFUlJfVFJBTlNGRVJfRkFJTEVEIDQpCihkZWZpbmUtY29uc3RhbnQgRVJSX0RJU0FMTE9XRURfQVNTRVQgNSkKKGRlZmluZS1jb25zdGFudCBFUlJfQVNTRVRfQUxSRUFEWV9BTExPV0VEIDYpCihkZWZpbmUtY29uc3RhbnQgRVJSX01FUktMRV9ST09UX0RPRVNfTk9UX01BVENIIDcpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QgOCkKKGRlZmluZS1jb25zdGFudCBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCA5KQooZGVmaW5lLWNvbnN0YW50IEVSUl9WQUxJREFUSU9OX0ZBSUxFRCAxMCkKOzs7IFRoZSB2YWx1ZSBzdXBwbGllZCBmb3IgYHRhcmdldC1jaGFpbi10aXBgIGRvZXMgbm90IG1hdGNoIHRoZSBjdXJyZW50IGNoYWluIHRpcC4KKGRlZmluZS1jb25zdGFudCBFUlJfSU5WQUxJRF9DSEFJTl9USVAgMTEpCjs7OyBUaGUgY29udHJhY3Qgd2FzIGNhbGxlZCBiZWZvcmUgcmVhY2hpbmcgdGhpcy1jaGFpbiBoZWlnaHQgcmVhY2hlcyAxLgooZGVmaW5lLWNvbnN0YW50IEVSUl9DQUxMRURfVE9PX0VBUkxZIDEyKQooZGVmaW5lLWNvbnN0YW50IEVSUl9NSU5UX0ZBSUxFRCAxMykKKGRlZmluZS1jb25zdGFudCBFUlJfQVRURU1QVF9UT19UUkFOU0ZFUl9aRVJPX0FNT1VOVCAxNCkKKGRlZmluZS1jb25zdGFudCBFUlJfSU5fQ09NUFVUQVRJT04gMTUpCjs7IFRoZSBjb250cmFjdCBkb2VzIG5vdCBvd24gdGhpcyBORlQgdG8gd2l0aGRyYXcgaXQuCihkZWZpbmUtY29uc3RhbnQgRVJSX05GVF9OT1RfT1dORURfQllfQ09OVFJBQ1QgMTYpCihkZWZpbmUtY29uc3RhbnQgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQgMzApCgo7OyBNYXAgZnJvbSBTdGFja3MgYmxvY2sgaGVpZ2h0IHRvIGJsb2NrIGNvbW1pdAooZGVmaW5lLW1hcCBibG9jay1jb21taXRzIHVpbnQgKGJ1ZmYgMzIpKQo7OyBNYXAgcmVjb3JkaW5nIHdpdGhkcmF3YWwgcm9vdHMKKGRlZmluZS1tYXAgd2l0aGRyYXdhbC1yb290cy1tYXAgKGJ1ZmYgMzIpIGJvb2wpCjs7IE1hcCByZWNvcmRpbmcgcHJvY2Vzc2VkIHdpdGhkcmF3YWwgbGVhdmVzCihkZWZpbmUtbWFwIHByb2Nlc3NlZC13aXRoZHJhd2FsLWxlYXZlcy1tYXAgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogKGJ1ZmYgMzIpLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogKGJ1ZmYgMzIpIH0gYm9vbCkKCjs7IHByaW5jaXBhbCB0aGF0IGNhbiBjb21taXQgYmxvY2tzCihkZWZpbmUtZGF0YS12YXIgbWluZXIgcHJpbmNpcGFsIHR4LXNlbmRlcikKOzsgcHJpbmNpcGFsIHRoYXQgY2FuIHJlZ2lzdGVyIGNvbnRyYWN0cwooZGVmaW5lLWRhdGEtdmFyIGFkbWluIHByaW5jaXBhbCB0eC1zZW5kZXIpCgo7OyBNYXAgb2YgYWxsb3dlZCBjb250cmFjdHMgZm9yIGFzc2V0IHRyYW5zZmVycyAtIG1hcHMgTDEgY29udHJhY3QgcHJpbmNpcGFsIHRvIEwyIGNvbnRyYWN0IHByaW5jaXBhbAooZGVmaW5lLW1hcCBhbGxvd2VkLWNvbnRyYWN0cyBwcmluY2lwYWwgcHJpbmNpcGFsKQoKOzsgVXNlIHRyYWl0IGRlY2xhcmF0aW9ucwoodXNlLXRyYWl0IG5mdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQubmZ0LXRyYWl0Lm5mdC10cmFpdCkKKHVzZS10cmFpdCBmdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQuc2lwLTAxMC10cmFpdC1mdC1zdGFuZGFyZC5zaXAtMDEwLXRyYWl0KQoodXNlLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQgLnN1Ym5ldC10cmFpdHMtdjEubWludC1mcm9tLXN1Ym5ldC10cmFpdCkKCjs7IFVwZGF0ZSB0aGUgbWluZXIgZm9yIHRoaXMgY29udHJhY3QuCihkZWZpbmUtcHVibGljICh1cGRhdGUtbWluZXIgKG5ldy1taW5lciBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgKGFzc2VydHMhIChpcy1lcSB0eC1zZW5kZXIgKHZhci1nZXQgbWluZXIpKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKICAgICAgICAob2sgKHZhci1zZXQgbWluZXIgbmV3LW1pbmVyKSkKICAgICkKKQoKOzsgUmVnaXN0ZXIgYSBuZXcgRlQgY29udHJhY3QgdG8gYmUgc3VwcG9ydGVkIGJ5IHRoaXMgc3VibmV0LgooZGVmaW5lLXB1YmxpYyAocmVnaXN0ZXItbmV3LWZ0LWNvbnRyYWN0IChmdC1jb250cmFjdCA8ZnQtdHJhaXQ+KSAobDItY29udHJhY3QgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFZlcmlmeSB0aGF0IHR4LXNlbmRlciBpcyBhbiBhdXRob3JpemVkIGFkbWluCiAgICAgICAgKGFzc2VydHMhIChpcy1hZG1pbiB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBTZXQgdXAgdGhlIGFzc2V0cyB0aGF0IHRoZSBjb250cmFjdCBpcyBhbGxvd2VkIHRvIHRyYW5zZmVyCiAgICAgICAgKGFzc2VydHMhIChtYXAtaW5zZXJ0IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAiZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdDogKGNvbnRyYWN0LW9mIGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbDItY29udHJhY3Q6IGwyLWNvbnRyYWN0CiAgICAgICAgfSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IFJlZ2lzdGVyIGEgbmV3IE5GVCBjb250cmFjdCB0byBiZSBzdXBwb3J0ZWQgYnkgdGhpcyBzdWJuZXQuCihkZWZpbmUtcHVibGljIChyZWdpc3Rlci1uZXctbmZ0LWNvbnRyYWN0IChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pIChsMi1jb250cmFjdCBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgOzsgVmVyaWZ5IHRoYXQgdHgtc2VuZGVyIGlzIGFuIGF1dGhvcml6ZWQgYWRtaW4KICAgICAgICAoYXNzZXJ0cyEgKGlzLWFkbWluIHR4LXNlbmRlcikgKGVyciBFUlJfSU5WQUxJRF9NSU5FUikpCgogICAgICAgIDs7IFNldCB1cCB0aGUgYXNzZXRzIHRoYXQgdGhlIGNvbnRyYWN0IGlzIGFsbG93ZWQgdG8gdHJhbnNmZXIKICAgICAgICAoYXNzZXJ0cyEgKG1hcC1pbnNlcnQgYWxsb3dlZC1jb250cmFjdHMgKGNvbnRyYWN0LW9mIG5mdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAibmZ0IiwKICAgICAgICAgICAgbDEtY29udHJhY3Q6IChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpLAogICAgICAgICAgICBsMi1jb250cmFjdDogbDItY29udHJhY3QKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhIG1pbmVyCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGlzLW1pbmVyIChtaW5lci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIG1pbmVyLXRvLWNoZWNrICh2YXItZ2V0IG1pbmVyKSkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhbiBhZG1pbgo7OyBSZXR1cm5zIGJvb2wKKGRlZmluZS1wcml2YXRlIChpcy1hZG1pbiAoYWRkci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIGFkZHItdG8tY2hlY2sgKHZhci1nZXQgYWRtaW4pKQopCgo7OyBIZWxwZXIgZnVuY3Rpb246IGRldGVybWluZXMgd2hldGhlciB0aGUgY29tbWl0LWJsb2NrIG9wZXJhdGlvbiBzYXRpc2ZpZXMgcHJlLWNvbmRpdGlvbnMKOzsgbGlzdGVkIGluIGBjb21taXQtYmxvY2tgLgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChjYW4tY29tbWl0LWJsb2NrPyAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KSAgKHRhcmdldC1jaGFpbi10aXAgKGJ1ZmYgMzIpKSkKICAgIChiZWdpbgogICAgICAgIDs7IGNoZWNrIG5vIGJsb2NrIGhhcyBiZWVuIGNvbW1pdHRlZCBhdCB0aGlzIGhlaWdodAogICAgICAgIChhc3NlcnRzISAoaXMtbm9uZSAobWFwLWdldD8gYmxvY2stY29tbWl0cyBjb21taXQtYmxvY2staGVpZ2h0KSkgKGVyciBFUlJfQkxPQ0tfQUxSRUFEWV9DT01NSVRURUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IGB0YXJnZXQtY2hhaW4tdGlwYCBtYXRjaGVzIHRoZSBidXJuIGNoYWluIHRpcAogICAgICAgIChhc3NlcnRzISAoaXMtZXEKICAgICAgICAgICAgdGFyZ2V0LWNoYWluLXRpcAogICAgICAgICAgICAodW53cmFwISAoZ2V0LWJsb2NrLWluZm8/IGlkLWhlYWRlci1oYXNoICgtIGJsb2NrLWhlaWdodCB1MSkpIChlcnIgRVJSX0NBTExFRF9UT09fRUFSTFkpKSApCiAgICAgICAgICAgIChlcnIgRVJSX0lOVkFMSURfQ0hBSU5fVElQKSkKCiAgICAgICAgOzsgY2hlY2sgdGhhdCB0aGUgdHggc2VuZGVyIGlzIG9uZSBvZiB0aGUgbWluZXJzCiAgICAgICAgKGFzc2VydHMhIChpcy1taW5lciB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSBtaW5lciBjYWxsZWQgdGhpcyBjb250cmFjdCBkaXJlY3RseQogICAgICAgIChhc3NlcnRzISAoaXMtbWluZXIgY29udHJhY3QtY2FsbGVyKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEhlbHBlciBmdW5jdGlvbjogbW9kaWZpZXMgdGhlIGJsb2NrLWNvbW1pdHMgbWFwIHdpdGggYSBuZXcgY29tbWl0IGFuZCBwcmludHMgcmVsYXRlZCBpbmZvCjs7IFJldHVybnMgcmVzcG9uc2U8KGJ1ZmYgMzIpLCA/PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLWNvbW1pdC1ibG9jawogICAgICAgIChibG9jayAoYnVmZiAzMikpCiAgICAgICAgKGNvbW1pdC1ibG9jay1oZWlnaHQgdWludCkKICAgICAgICAodGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICApCiAgICAoYmVnaW4KICAgICAgICAobWFwLXNldCBibG9jay1jb21taXRzIGNvbW1pdC1ibG9jay1oZWlnaHQgYmxvY2spCiAgICAgICAgKG1hcC1zZXQgd2l0aGRyYXdhbC1yb290cy1tYXAgd2l0aGRyYXdhbC1yb290IHRydWUpCiAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgZXZlbnQ6ICJibG9jay1jb21taXQiLAogICAgICAgICAgICBibG9jay1jb21taXQ6IGJsb2NrLAogICAgICAgICAgICBibG9jay1oZWlnaHQ6IGNvbW1pdC1ibG9jay1oZWlnaHQsCiAgICAgICAgICAgIHdpdGhkcmF3YWwtcm9vdDogd2l0aGRyYXdhbC1yb290LAogICAgICAgICAgICB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQ6IHRhcmdldC1idXJuLWJsb2NrLWhlaWdodAogICAgICAgIH0pCiAgICAgICAgKG9rIGJsb2NrKQogICAgKQopCgo7OyBUaGUgc3VibmV0IG1pbmVyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gY29tbWl0IGEgYmxvY2sgYXQgYSBwYXJ0aWN1bGFyIGhlaWdodC4KOzsgYGJsb2NrYCBpcyB0aGUgaGFzaCBvZiB0aGUgYmxvY2sgYmVpbmcgc3VibWl0dGVkLgo7OyBgdGFyZ2V0LWNoYWluLXRpcGAgaXMgdGhlIGBpZC1oZWFkZXItaGFzaGAgb2YgdGhlIGJ1cm4gYmxvY2sgKGkuZS4sIGJsb2NrIG9uCjs7ICAgIHRoaXMgY2hhaW4pIHRoYXQgdGhlIG1pbmVyIGludGVuZHMgdG8gYnVpbGQgb2ZmLgo7Owo7OyBGYWlscyBpZjoKOzsgIDEpIHdlIGhhdmUgYWxyZWFkeSBjb21taXR0ZWQgYXQgdGhpcyBibG9jayBoZWlnaHQKOzsgIDIpIGB0YXJnZXQtY2hhaW4tdGlwYCBpcyBub3QgdGhlIGJ1cm4gY2hhaW4gdGlwIChpLmUuLCBvbiB0aGlzIGNoYWluKQo7OyAgMykgdGhlIHNlbmRlciBpcyBub3QgYSBtaW5lcgooZGVmaW5lLXB1YmxpYyAoY29tbWl0LWJsb2NrCiAgICAgICAgKGJsb2NrIChidWZmIDMyKSkKICAgICAgICAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KQogICAgICAgICh0YXJnZXQtY2hhaW4tdGlwIChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKCh0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgYmxvY2staGVpZ2h0KSkKICAgICAgICAodHJ5ISAoY2FuLWNvbW1pdC1ibG9jaz8gdGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHRhcmdldC1jaGFpbi10aXApKQogICAgICAgIChpbm5lci1jb21taXQtYmxvY2sgYmxvY2sgY29tbWl0LWJsb2NrLWhlaWdodCB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgd2l0aGRyYXdhbC1yb290KQogICAgKQopCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBORlQgQVNTRVQgVFJBTlNGRVJTCgo7OyBIZWxwZXIgZnVuY3Rpb24gdGhhdCB0cmFuc2ZlcnMgdGhlIHNwZWNpZmllZCBORlQgZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgdHJhbnNmZXIgaWQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChhcy1jb250cmFjdCAoY29udHJhY3QtY2FsbD8gbmZ0LW1pbnQtY29udHJhY3QgbWludC1mcm9tLXN1Ym5ldCBpZCBzZW5kZXIgcmVjaXBpZW50KSkpCiAgICAgICAgICAgIChtaW50LXJlc3VsdCAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICkKICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB0cmFuc2ZlciBzdWNjZWVkZWQKICAgICAgICAoYXNzZXJ0cyEgbWludC1yZXN1bHQgKGVyciBFUlJfTUlOVF9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikKICAgICAgICAoaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGNvbnRyYWN0LWNhbGw/IG5mdC1jb250cmFjdCBnZXQtb3duZXIgaWQpKQogICAgICAgICAgICAobmZ0LW93bmVyICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLW5mdCAoaXMtZXEgbmZ0LW93bmVyIChzb21lIENPTlRSQUNUX0FERFJFU1MpKSkKICAgICAgICAgICAgKG5vLW93bmVyIChpcy1lcSBuZnQtb3duZXIgbm9uZSkpCiAgICAgICAgKQoKICAgICAgICAoaWYgY29udHJhY3Qtb3ducy1uZnQKICAgICAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgIChpZiBuby1vd25lcgogICAgICAgICAgICAgICAgOzsgVHJ5IG1pbnRpbmcgdGhlIGFzc2V0IGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG93bmVyIG9mIHRoaXMgTkZUCiAgICAgICAgICAgICAgICAoaW5uZXItbWludC1uZnQtYXNzZXQgbmZ0LW1pbnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgICAgICA7OyBJbiB0aGlzIGNhc2UsIGEgcHJpbmNpcGFsIG90aGVyIHRoYW4gdGhpcyBjb250cmFjdCBvd25zIHRoaXMgTkZULCBzbyBtaW50aW5nIGlzIG5vdCBwb3NzaWJsZQogICAgICAgICAgICAgICAgKGVyciBFUlJfTUlOVF9GQUlMRUQpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgYW4gTkZUIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQoKICAgICAgICA7OyBUcnkgdG8gdHJhbnNmZXIgdGhlIE5GVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1uZnQtYXNzZXQgbmZ0LWNvbnRyYWN0IGlkIHNlbmRlciBDT05UUkFDVF9BRERSRVNTKSkgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgOzsgRW1pdCBhIHByaW50IGV2ZW50IC0gdGhlIG5vZGUgY29uc3VtZXMgdGhpcwogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1uZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IG5mdC1jb250cmFjdCksCiAgICAgICAgICAgIG5mdC1pZDogaWQsCiAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICBzdWJuZXQtY29udHJhY3QtaWQ6IHN1Ym5ldC1jb250cmFjdC1pZCwKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IEhlbHBlciBmdW5jdGlvbiBmb3IgYHdpdGhkcmF3LW5mdC1hc3NldGAKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChpbm5lci13aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChsMi1jb250cmFjdCBwcmluY2lwYWwpCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAobGV0ICgoaGFzaGVzLWFyZS12YWxpZCAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMgd2l0aGRyYXdhbC1yb290IHdpdGhkcmF3YWwtbGVhZi1oYXNoIHNpYmxpbmctaGFzaGVzKSkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISBoYXNoZXMtYXJlLXZhbGlkKSAoZXJyIEVSUl9WQUxJREFUSU9OX0ZBSUxFRCkpCgogICAgICAgIDs7IGNoZWNrIHRoYXQgdGhlIHdpdGhkcmF3YWwgcmVxdWVzdCBkYXRhIG1hdGNoZXMgdGhlIHN1cHBsaWVkIGxlYWYgaGFzaAogICAgICAgIChhc3NlcnRzISAoaXMtZXEgd2l0aGRyYXdhbC1sZWFmLWhhc2gKICAgICAgICAgICAgICAgICAgICAgICAgIChsZWFmLWhhc2gtd2l0aGRyYXctbmZ0IGwyLWNvbnRyYWN0IGlkIHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBuZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgICAgIG1pbnQtY29udHJhY3QgKGFzLWNvbnRyYWN0IChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgbWludC1jb250cmFjdCBpZCByZWNpcGllbnQpKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgcmVjaXBpZW50KSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKQogICAgICAgICkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIHdpdGhkcmF3IHRoZSBzcGVjaWZpZWQgTkZUIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChpZCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChuZnQtbWludC1jb250cmFjdCAob3B0aW9uYWwgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIGFzc2V0IGJlbG9uZ3MgdG8gdGhlIGFsbG93ZWQtY29udHJhY3RzIG1hcAogICAgICAgICAgICAobDItY29udHJhY3QgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgICAodHJ5ISAoaW5uZXItd2l0aGRyYXctbmZ0LWFzc2V0CiAgICAgICAgICAgICAgICBuZnQtY29udHJhY3QKICAgICAgICAgICAgICAgIGwyLWNvbnRyYWN0CiAgICAgICAgICAgICAgICBpZAogICAgICAgICAgICAgICAgcmVjaXBpZW50CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWlkCiAgICAgICAgICAgICAgICBoZWlnaHQKICAgICAgICAgICAgICAgIG5mdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLXJvb3QKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICBzaWJsaW5nLWhhc2hlcwogICAgICAgICAgICApKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgewogICAgICAgICAgICBldmVudDogIndpdGhkcmF3LW5mdCIsCiAgICAgICAgICAgIGwxLWNvbnRyYWN0LWlkOiAoYXMtY29udHJhY3QgbmZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbmZ0LWlkOiBpZCwKICAgICAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IExpa2UgYGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtbmZ0LWFzc2V0IGJ1dCB3aXRob3V0IGFsbG93aW5nIG9yIHJlcXVpcmluZyBhIG1pbnQgZnVuY3Rpb24uIEluIG9yZGVyIHRvIHdpdGhkcmF3LCB0aGUgdXNlciBtdXN0Cjs7IGhhdmUgdGhlIGFwcHJvcHJpYXRlIGJhbGFuY2UuCihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgZ2V0LW93bmVyIGlkKSkKICAgICAgICAgICAgKG5mdC1vd25lciAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICAoY29udHJhY3Qtb3ducy1uZnQgKGlzLWVxIG5mdC1vd25lciAoc29tZSBDT05UUkFDVF9BRERSRVNTKSkpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEgY29udHJhY3Qtb3ducy1uZnQgKGVyciBFUlJfTkZUX05PVF9PV05FRF9CWV9DT05UUkFDVCkpCiAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICApCikKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgRk9SIEZVTkdJQkxFIFRPS0VOIEFTU0VUIFRSQU5TRkVSUwoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aGUgZnVuZ2libGUgdG9rZW4gZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChzZW5kZXIgcHJpbmNpcGFsKQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgdHJhbnNmZXIgYW1vdW50IHNlbmRlciByZWNpcGllbnQgbWVtbykpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgRklYTUU6IFNJUC0wMTAgZG9lc24ndCByZXF1aXJlIHRoYXQgdHJhbnNmZXIgcmV0dXJucyAob2sgdHJ1ZSkgb24gc3VjY2Vzcywgc28gaXMgdGhpcyBjaGVjayBuZWNlc3Nhcnk/CiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LWZ0LWFzc2V0CiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAoc2VuZGVyIHByaW5jaXBhbCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGFzLWNvbnRyYWN0IChjb250cmFjdC1jYWxsPyBmdC1taW50LWNvbnRyYWN0IG1pbnQtZnJvbS1zdWJuZXQgYW1vdW50IHNlbmRlciByZWNpcGllbnQpKSkKICAgICAgICAgICAgKG1pbnQtcmVzdWx0ICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISBtaW50LXJlc3VsdCAoZXJyIEVSUl9NSU5UX0ZBSUxFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgZ2V0LWJhbGFuY2UgQ09OVFJBQ1RfQUREUkVTUykpCiAgICAgICAgICAgIChjb250cmFjdC1mdC1iYWxhbmNlICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLWVub3VnaCAoPj0gY29udHJhY3QtZnQtYmFsYW5jZSBhbW91bnQpKQogICAgICAgICAgICAoYW1vdW50LXRvLXRyYW5zZmVyIChpZiBjb250cmFjdC1vd25zLWVub3VnaCBhbW91bnQgY29udHJhY3QtZnQtYmFsYW5jZSkpCiAgICAgICAgICAgIChhbW91bnQtdG8tbWludCAoLSBhbW91bnQgYW1vdW50LXRvLXRyYW5zZmVyKSkKICAgICAgICApCgogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRvdGFsIGJhbGFuY2UgYmV0d2VlbiB0aGUgdHJhbnNmZXIgYW5kIG1pbnQgaXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIGJhbGFuY2UKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIGFtb3VudCAoKyBhbW91bnQtdG8tdHJhbnNmZXIgYW1vdW50LXRvLW1pbnQpKSAoZXJyIEVSUl9JTl9DT01QVVRBVElPTikpCgogICAgICAgIChhbmQKICAgICAgICAgICAgKD4gYW1vdW50LXRvLXRyYW5zZmVyIHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50LXRvLXRyYW5zZmVyIENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICkKICAgICAgICAoYW5kCiAgICAgICAgICAgICg+IGFtb3VudC10by1taW50IHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItbWludC1mdC1hc3NldCBmdC1taW50LWNvbnRyYWN0IGFtb3VudC10by1taW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50KSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FsbHMgdGhpcyBmdW5jdGlvbiB0byBkZXBvc2l0IGEgZnVuZ2libGUgdG9rZW4gaW50byB0aGUgY29udHJhY3QuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKGRlcG9zaXQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKSkKICAgICAgICApCiAgICAgICAgOzsgVHJ5IHRvIHRyYW5zZmVyIHRoZSBGVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1mdC1hc3NldCBmdC1jb250cmFjdCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MgbWVtbykpIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkpCgogICAgICAgIChsZXQgKAogICAgICAgICAgICAgICAgKGZ0LW5hbWUgKHVud3JhcCEgKGNvbnRyYWN0LWNhbGw/IGZ0LWNvbnRyYWN0IGdldC1uYW1lKSAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICApCiAgICAgICAgICAgIDs7IEVtaXQgYSBwcmludCBldmVudCAtIHRoZSBub2RlIGNvbnN1bWVzIHRoaXMKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1mdCIsCiAgICAgICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgICAgIGZ0LW5hbWU6IGZ0LW5hbWUsCiAgICAgICAgICAgICAgICBmdC1hbW91bnQ6IGFtb3VudCwKICAgICAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICAgICAgc3VibmV0LWNvbnRyYWN0LWlkOiBzdWJuZXQtY29udHJhY3QtaWQsCiAgICAgICAgICAgIH0pCiAgICAgICAgKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBwZXJmb3JtcyB2YWxpZGl0eSBjaGVja3MgcmVsYXRlZCB0byB0aGUgd2l0aGRyYXdhbCBhbmQgcGVyZm9ybXMgdGhlIHdpdGhkcmF3YWwgYXMgd2VsbC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItd2l0aGRyYXctZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgKG9wdGlvbmFsIDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgaGFzaGVzLWFyZS12YWxpZCkgKGVyciBFUlJfVkFMSURBVElPTl9GQUlMRUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LWZ0IChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgYW1vdW50IHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBmdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICAgICAgbWludC1jb250cmFjdCAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQgZnQtY29udHJhY3QgbWludC1jb250cmFjdCBhbW91bnQgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICAgICAgICAgIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkKICAgICAgICApCgogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgKGVyciBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB3aXRoZHJhdyBzb21lIGFtb3VudCBvZiBhIGZ1bmdpYmxlIHRva2VuIGFzc2V0IGZyb20gdGhlCjs7IGNvbnRyYWN0IGFuZCBzZW5kIGl0IHRvIGEgcmVjaXBpZW50Lgo7OyBJbiBvcmRlciBmb3IgdGhpcyB3aXRoZHJhd2FsIHRvIGdvIHRocm91Z2gsIHRoZSBnaXZlbiB3aXRoZHJhd2FsIG11c3QgaGF2ZSBiZWVuIGluY2x1ZGVkCjs7IGluIGEgd2l0aGRyYXdhbCBNZXJrbGUgdHJlZSBhIHN1Ym5ldCBtaW5lciBzdWJtaXR0ZWQuIFRoZSB1c2VyIG11c3QgcHJvdmlkZSB0aGUgbGVhZgo7OyBoYXNoIG9mIHRoZWlyIHdpdGhkcmF3YWwgYW5kIHRoZSByb290IGhhc2ggb2YgdGhlIHNwZWNpZmljIE1lcmtsZSB0cmVlIHRoZWlyIHdpdGhkcmF3YWwKOzsgaXMgaW5jbHVkZWQgaW4uIFRoZXkgbXVzdCBhbHNvIHByb3ZpZGUgYSBsaXN0IG9mIHNpYmxpbmcgaGFzaGVzLiBUaGUgd2l0aGRyYXcgZnVuY3Rpb24KOzsgdXNlcyB0aGUgcHJvdmlkZWQgaGFzaGVzIHRvIGVuc3VyZSB0aGUgcmVxdWVzdGVkIHdpdGhkcmF3YWwgaXMgdmFsaWQuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKHdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgICAgIChmdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAoYmVnaW4KICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB3aXRoZHJhdyBhbW91bnQgaXMgcG9zaXRpdmUKICAgICAgICAoYXNzZXJ0cyEgKD4gYW1vdW50IHUwKSAoZXJyIEVSUl9BVFRFTVBUX1RPX1RSQU5TRkVSX1pFUk9fQU1PVU5UKSkKCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEgKGlubmVyLXdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgICAgICAgICBmdC1jb250cmFjdAogICAgICAgICAgICAgICAgYW1vdW50CiAgICAgICAgICAgICAgICByZWNpcGllbnQKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtaWQKICAgICAgICAgICAgICAgIGhlaWdodAogICAgICAgICAgICAgICAgbWVtbwogICAgICAgICAgICAgICAgZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWxlYWYtaGFzaAogICAgICAgICAgICAgICAgc2libGluZy1oYXNoZXMpKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAobGV0ICgKICAgICAgICAgICAgICAgIChmdC1uYW1lICh1bndyYXAhIChjb250cmFjdC1jYWxsPyBmdC1jb250cmFjdCBnZXQtbmFtZSkgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICAgICAgKQogICAgICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAid2l0aGRyYXctZnQiLAogICAgICAgICAgICAgICAgbDEtY29udHJhY3QtaWQ6IChhcy1jb250cmFjdCBmdC1jb250cmFjdCksCiAgICAgICAgICAgICAgICBmdC1uYW1lOiBmdC1uYW1lLAogICAgICAgICAgICAgICAgZnQtYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgfSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgoKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBTVFggVFJBTlNGRVJTCgoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIHRoZSBnaXZlbiBhbW91bnQgZnJvbSB0aGUgc3BlY2lmaWVkIGZ1bmdpYmxlIHRva2VuIGZyb20gdGhlIGdpdmVuIHNlbmRlciB0byB0aGUgZ2l2ZW4gcmVjaXBpZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1zdHggKGFtb3VudCB1aW50KSAoc2VuZGVyIHByaW5jaXBhbCkgKHJlY2lwaWVudCBwcmluY2lwYWwpKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoc3R4LXRyYW5zZmVyPyBhbW91bnQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISB0cmFuc2Zlci1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgU1RYIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LXN0eCAoYW1vdW50IHVpbnQpIChzZW5kZXIgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFRyeSB0byB0cmFuc2ZlciB0aGUgU1RYIHRvIHRoaXMgY29udHJhY3QKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQgLSB0aGUgbm9kZSBjb25zdW1lcyB0aGlzCiAgICAgICAgKHByaW50IHsgZXZlbnQ6ICJkZXBvc2l0LXN0eCIsIHNlbmRlcjogc2VuZGVyLCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICApCiAgICAoc2hhNTEyLzI1NiAoY29uY2F0IDB4MDAgKHVud3JhcC1wYW5pYyAodG8tY29uc2Vuc3VzLWJ1ZmY/CiAgICAgICAgewogICAgICAgICAgICB0eXBlOiAic3R4IiwKICAgICAgICAgICAgYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1uZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChuZnQtaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJuZnQiLAogICAgICAgICAgICBuZnQtaWQ6IG5mdC1pZCwKICAgICAgICAgICAgYXNzZXQtY29udHJhY3Q6IGFzc2V0LWNvbnRyYWN0LAogICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgd2l0aGRyYXdhbC1pZDogd2l0aGRyYXdhbC1pZCwKICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQKICAgICAgICB9KSkpCiAgICApCikKCihkZWZpbmUtcmVhZC1vbmx5IChsZWFmLWhhc2gtd2l0aGRyYXctZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJmdCIsCiAgICAgICAgICAgIGFtb3VudDogYW1vdW50LAogICAgICAgICAgICBhc3NldC1jb250cmFjdDogYXNzZXQtY29udHJhY3QsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKOzsgQSB1c2VyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gd2l0aGRyYXcgU1RYIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKCiAgICAgICAgKGFzc2VydHMhICh0cnkhIGhhc2hlcy1hcmUtdmFsaWQpIChlcnIgRVJSX1ZBTElEQVRJT05fRkFJTEVEKSkKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LXN0eCBhbW91bnQgcmVjaXBpZW50IHdpdGhkcmF3YWwtaWQgaGVpZ2h0KSkKICAgICAgICAgICAgICAgICAgKGVyciBFUlJfVkFMSURBVElPTl9MRUFGX0ZBSUxFRCkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgdHgtc2VuZGVyIHJlY2lwaWVudCkpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgIChmaW5pc2gtd2l0aGRyYXcgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogd2l0aGRyYXdhbC1sZWFmLWhhc2gsIHdpdGhkcmF3YWwtcm9vdC1oYXNoOiB3aXRoZHJhd2FsLXJvb3QgfSkKICAgICAgICAgIChlcnIgRVJSX1dJVEhEUkFXQUxfQUxSRUFEWV9QUk9DRVNTRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgeyBldmVudDogIndpdGhkcmF3LXN0eCIsIHJlY2lwaWVudDogcmVjaXBpZW50LCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBHRU5FUkFMIFdJVEhEUkFXQUwgRlVOQ1RJT05TCgo7OyBUaGlzIGZ1bmN0aW9uIGNvbmNhdHMgdGhlIHR3byBnaXZlbiBoYXNoZXMgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuIEl0IGFsc28gcHJlcGVuZHMgdGhlIGJ1ZmYgYDB4MDFgLCB3aGljaCBpcwo7OyBhIHRhZyBkZW5vdGluZyBhIG5vZGUgKHZlcnN1cyBhIGxlYWYpLgo7OyBSZXR1cm5zIGEgYnVmZgooZGVmaW5lLXByaXZhdGUgKGNyZWF0ZS1ub2RlLWhhc2gKICAgICAgICAoY3Vyci1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoIChidWZmIDMyKSkKICAgICAgICAoaXMtc2libGluZy1sZWZ0LXNpZGUgYm9vbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY29uY2F0dGVkLWhhc2ggKGlmIGlzLXNpYmxpbmctbGVmdC1zaWRlCiAgICAgICAgICAgICAgICAgICAgKGNvbmNhdCBzaWJsaW5nLWhhc2ggY3Vyci1oYXNoKQogICAgICAgICAgICAgICAgICAgIChjb25jYXQgY3Vyci1oYXNoIHNpYmxpbmctaGFzaCkKICAgICAgICAgICAgICAgICkpCiAgICAgICAgICApCgogICAgICAgICAgKGNvbmNhdCAweDAxIGNvbmNhdHRlZC1oYXNoKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIGhhc2hlcyB0aGUgY3VyciBoYXNoIHdpdGggaXRzIHNpYmxpbmcgaGFzaC4KOzsgUmV0dXJucyAoYnVmZiAzMikKKGRlZmluZS1wcml2YXRlIChoYXNoLWhlbHAKICAgICAgICAoc2libGluZyB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pCiAgICAgICAgKGN1cnItbm9kZS1oYXNoIChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoc2libGluZy1oYXNoIChnZXQgaGFzaCBzaWJsaW5nKSkKICAgICAgICAgICAgKGlzLXNpYmxpbmctbGVmdC1zaWRlIChnZXQgaXMtbGVmdC1zaWRlIHNpYmxpbmcpKQogICAgICAgICAgICAobmV3LWJ1ZmYgKGNyZWF0ZS1ub2RlLWhhc2ggY3Vyci1ub2RlLWhhc2ggc2libGluZy1oYXNoIGlzLXNpYmxpbmctbGVmdC1zaWRlKSkKICAgICAgICApCiAgICAgICAoc2hhNTEyLzI1NiBuZXctYnVmZikKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBjaGVja3M6Cjs7ICAtIFRoYXQgdGhlIHByb3ZpZGVkIHdpdGhkcmF3YWwgcm9vdCBtYXRjaGVzIGEgcHJldmlvdXNseSBzdWJtaXR0ZWQgb25lIChwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uIGBjb21taXQtYmxvY2tgKQo7OyAgLSBUaGF0IHRoZSBjb21wdXRlZCB3aXRoZHJhd2FsIHJvb3QgbWF0Y2hlcyBhIHByZXZpb3VzIHZhbGlkIHdpdGhkcmF3YWwgcm9vdAo7OyAgLSBUaGF0IHRoZSBnaXZlbiB3aXRoZHJhd2FsIGxlYWYgaGFzaCBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBwcm9jZXNzZWQKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGJlZ2luCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdXNlciBzdWJtaXR0ZWQgYSB2YWxpZCB3aXRoZHJhd2FsIHJvb3QKICAgICAgICAoYXNzZXJ0cyEgKGlzLXNvbWUgKG1hcC1nZXQ/IHdpdGhkcmF3YWwtcm9vdHMtbWFwIHdpdGhkcmF3YWwtcm9vdCkpIChlcnIgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QpKQoKICAgICAgICA7OyBDaGVjayB0aGF0IHRoaXMgd2l0aGRyYXdhbCBsZWFmIGhhcyBub3QgYmVlbiBwcm9jZXNzZWQgYmVmb3JlCiAgICAgICAgKGFzc2VydHMhCiAgICAgICAgICAgIChpcy1ub25lCiAgICAgICAgICAgICAobWFwLWdldD8gcHJvY2Vzc2VkLXdpdGhkcmF3YWwtbGVhdmVzLW1hcAogICAgICAgICAgICAgICAgICAgICAgIHsgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IHdpdGhkcmF3YWwtbGVhZi1oYXNoLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogd2l0aGRyYXdhbC1yb290IH0pKQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKSkKCiAgICAgICAgKGxldCAoKGNhbGN1bGF0ZWQtd2l0aGRyYXdhbC1yb290IChmb2xkIGhhc2gtaGVscCBzaWJsaW5nLWhhc2hlcyB3aXRoZHJhd2FsLWxlYWYtaGFzaCkpCiAgICAgICAgICAgICAgKHJvb3RzLW1hdGNoIChpcy1lcSBjYWxjdWxhdGVkLXdpdGhkcmF3YWwtcm9vdCB3aXRoZHJhd2FsLXJvb3QpKSkKICAgICAgICAgICAgIChpZiByb290cy1tYXRjaAogICAgICAgICAgICAgICAgKG9rIHRydWUpCiAgICAgICAgICAgICAgICAoZXJyIEVSUl9NRVJLTEVfUk9PVF9ET0VTX05PVF9NQVRDSCkpCiAgICAgICAgKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBjYWxsZWQgYWZ0ZXIgdGhlIGFzc2V0IGluIHF1ZXN0aW9uIGhhcyBiZWVuIHRyYW5zZmVycmVkLgo7OyBJdCBhZGRzIHRoZSB3aXRoZHJhd2FsIGxlYWYgaGFzaCB0byBhIG1hcCBvZiBwcm9jZXNzZWQgbGVhdmVzLiBUaGlzIGVuc3VyZXMgdGhhdAo7OyB0aGlzIHdpdGhkcmF3YWwgbGVhZiBjYW4ndCBiZSB1c2VkIGFnYWluIHRvIHdpdGhkcmF3IGFkZGl0aW9uYWwgZnVuZHMuCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGZpbmlzaC13aXRoZHJhdwogICAgICAgICh3aXRoZHJhdy1pbmZvIHsKICAgICAgICAgICAgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290LWhhc2g6IChidWZmIDMyKQogICAgICAgIH0pCiAgICApCiAgICAobWFwLWluc2VydCBwcm9jZXNzZWQtd2l0aGRyYXdhbC1sZWF2ZXMtbWFwIHdpdGhkcmF3LWluZm8gdHJ1ZSkKKQo= + clarity_version: 2 + cost: 290960 + location: + path: "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2.clar" + - transaction_type: ContractPublish + contract_name: px + expected_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + location: + path: contracts/px.clar + source: Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK + clarity_version: 2 + cost: 18060 + anchor_block_only: true + epoch: "2.1" + - id: 3 + transactions: + - transaction_type: ContractCall + contract_id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px + expected_sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + method: set-value-at + parameters: + - u0 + - "0xfffffa" + cost: 2240 + anchor_block_only: false + - transaction_type: StxTransfer + expected_sender: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB + recipient: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 + mstx_amount: 200 + memo: "0xabcabcabcaba00000000000000000000000000000000000000000000000000000000" + cost: 2240 + anchor_block_only: true + epoch: "2.1" +contracts: + - contract_id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px + path: /Users/micaiahreid/work/stx-px/contracts/px.clar + source: Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK diff --git a/src/tests/fixtures/network-manifest.yaml b/src/tests/fixtures/network-manifest.yaml index b68c31c..e4d84b7 100644 --- a/src/tests/fixtures/network-manifest.yaml +++ b/src/tests/fixtures/network-manifest.yaml @@ -1,31 +1,177 @@ -[network] -name = 'devnet' - -[accounts.deployer] -mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" -balance = "100000000000000" - - -[accounts.wallet_1] -mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild" -balance = "100000000000000" - -[devnet] -miner_mnemonic = "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild" -miner_derivation_path = "m/44'/5757'/0'/0/0" -bitcoin_node_username = "test-username" -bitcoin_node_password = "test-password" -faucet_mnemonic = "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform" -faucet_derivation_path = "m/44'/5757'/0'/0/0" -orchestrator_ingestion_port = 20445 -orchestrator_control_port = 20446 -bitcoin_node_rpc_port = 18443 -stacks_node_rpc_port = 20443 -stacks_api_port = 3999 -epoch_2_0 = 100 -epoch_2_05 = 102 -epoch_2_1 = 106 -epoch_2_2 = 120 -working_dir = "/devnet" -bitcoin_controller_block_time = 0 -bitcoin_controller_automining_disabled = true \ No newline at end of file +--- +network: + name: devnet + stacks_node_rpc_address: ~ + bitcoin_node_rpc_address: ~ + deployment_fee_rate: 10 + sats_per_bytes: 10 +accounts: + - label: deployer + mnemonic: twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH + is_mainnet: false + - label: faucet + mnemonic: shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 + btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + is_mainnet: false + - label: wallet_1 + mnemonic: crazy vibrant runway diagram beach language above aerobic maze coral this gas mirror output vehicle cover usage ecology unfold room feel file rocket expire + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: STB8E0SMACY4A6DCCH4WE48YGX3P877407QW176V + btc_address: mha4u7F3e93P9Xy1WQgVvGtYtynnJtT22x + is_mainnet: false + - label: wallet_2 + mnemonic: hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG + btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + is_mainnet: false + - label: wallet_3 + mnemonic: cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC + btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + is_mainnet: false + - label: wallet_4 + mnemonic: board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND + btc_address: mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8 + is_mainnet: false + - label: wallet_5 + mnemonic: hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB + btc_address: mweN5WVqadScHdA81aATSdcVr4B6dNokqx + is_mainnet: false + - label: wallet_6 + mnemonic: area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0 + btc_address: mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt + is_mainnet: false + - label: wallet_7 + mnemonic: prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ + btc_address: n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7 + is_mainnet: false + - label: wallet_8 + mnemonic: female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune + derivation: "m/44'/5757'/0'/0/0" + balance: 100000000000000 + stx_address: ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP + btc_address: n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw + is_mainnet: false +devnet_settings: + name: devnet + network_id: ~ + orchestrator_ingestion_port: 20445 + orchestrator_control_port: 20446 + bitcoin_node_p2p_port: 18444 + bitcoin_node_rpc_port: 18443 + bitcoin_node_username: devnet + bitcoin_node_password: devnet + stacks_node_p2p_port: 20444 + stacks_node_rpc_port: 20443 + stacks_node_wait_time_for_microblocks: 50 + stacks_node_first_attempt_time_ms: 500 + stacks_node_subsequent_attempt_time_ms: 1000 + stacks_node_events_observers: + - "host.docker.internal:20455" + stacks_node_env_vars: [] + stacks_api_port: 3999 + stacks_api_events_port: 3700 + stacks_api_env_vars: [] + stacks_explorer_port: 8000 + stacks_explorer_env_vars: [] + bitcoin_explorer_port: 8001 + bitcoin_controller_block_time: 60000 + bitcoin_controller_automining_disabled: false + miner_stx_address: ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ + miner_secret_key_hex: 3b68e410cc7f9b8bae76f2f2991b69ecd0627c95da22a904065dfb2a73d0585f01 + miner_btc_address: n3GRiDLKWuKLCw1DZmV75W1mE35qmW2tQm + miner_mnemonic: fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce + miner_derivation_path: "m/44'/5757'/0'/0/0" + miner_coinbase_recipient: ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ + faucet_stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 + faucet_secret_key_hex: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801 + faucet_btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + faucet_mnemonic: shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform + faucet_derivation_path: "m/44'/5757'/0'/0/0" + working_dir: /Users/micaiahreid/work/stx-px/tmp + postgres_port: 5432 + postgres_username: postgres + postgres_password: postgres + stacks_api_postgres_database: stacks_api + subnet_api_postgres_database: subnet_api + pox_stacking_orders: + - start_at_cycle: 3 + duration: 12 + wallet: wallet_1 + slots: 2 + btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC + - start_at_cycle: 3 + duration: 12 + wallet: wallet_2 + slots: 1 + btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + - start_at_cycle: 3 + duration: 12 + wallet: wallet_3 + slots: 1 + btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + execute_script: [] + bitcoin_node_image_url: "quay.io/hirosystems/bitcoind:devnet-v3" + stacks_node_image_url: "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0" + stacks_api_image_url: "hirosystems/stacks-blockchain-api:latest" + stacks_explorer_image_url: "hirosystems/explorer:latest" + postgres_image_url: "postgres:14" + bitcoin_explorer_image_url: "quay.io/hirosystems/bitcoin-explorer:devnet" + disable_bitcoin_explorer: true + disable_stacks_explorer: true + disable_stacks_api: false + bind_containers_volumes: false + enable_subnet_node: false + subnet_node_image_url: "hirosystems/stacks-subnets:0.8.1" + subnet_leader_stx_address: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + subnet_leader_secret_key_hex: 753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601 + subnet_leader_btc_address: mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH + subnet_leader_mnemonic: twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw + subnet_leader_derivation_path: "m/44'/5757'/0'/0/0" + subnet_node_p2p_port: 30444 + subnet_node_rpc_port: 30443 + subnet_events_ingestion_port: 30445 + subnet_node_events_observers: [] + subnet_contract_id: ST173JK7NZBA4BS05ZRATQH1K89YJMTGEH1Z5J52E.subnet-v3-0-1 + remapped_subnet_contract_id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet-v3-0-1 + subnet_node_env_vars: [] + subnet_api_image_url: "hirosystems/stacks-blockchain-api:latest" + subnet_api_port: 13999 + subnet_api_events_port: 13700 + subnet_api_env_vars: [] + disable_subnet_api: true + docker_host: "unix:///var/run/docker.sock" + components_host: 127.0.0.1 + epoch_2_0: 100 + epoch_2_05: 100 + epoch_2_1: 101 + epoch_2_2: 103 + epoch_2_3: 104 + epoch_2_4: 105 + pox_2_activation: 102 + use_docker_gateway_routing: false + docker_platform: linux/amd64 diff --git a/src/tests/fixtures/project-manifest.yaml b/src/tests/fixtures/project-manifest.yaml index 5acdeab..15a648b 100644 --- a/src/tests/fixtures/project-manifest.yaml +++ b/src/tests/fixtures/project-manifest.yaml @@ -1,10 +1,23 @@ -[project] -name = "px" -description = "my description" -authors = ['test1','test2'] -requirements = ['test1','test2'] - -[contracts.px] -path = "contracts/px.clar" -clarity_version = 2 -epoch = "2.1" \ No newline at end of file +--- +metadata: + name: px + description: my description + authors: + - test1 + - test2 + telemetry: false + cache_dir: /etc/stacks-network/project/contracts + requirements: [] +contracts: + px: + path: contracts/px.clar + clarity_version: 2 + epoch: 2.1 +repl: + analysis: + passes: [] + check_checker: + strict: false + trusted_sender: false + trusted_caller: false + callee_filter: false diff --git a/src/tests/fixtures/stacks-devnet-config.json b/src/tests/fixtures/stacks-devnet-config.json index 08de413..860d6f9 100644 --- a/src/tests/fixtures/stacks-devnet-config.json +++ b/src/tests/fixtures/stacks-devnet-config.json @@ -1,80 +1,366 @@ { - "namespace": "test-namespace1", - "stacks_node_wait_time_for_microblocks": 50, - "stacks_node_first_attempt_time_ms": 500, - "stacks_node_subsequent_attempt_time_ms": 1000, - "bitcoin_node_username": "test-username", - "bitcoin_node_password": "test-password", - "miner_mnemonic": "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild", - "miner_derivation_path": "m/44'/5757'/0'/0/0", - "miner_coinbase_recipient": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", - "faucet_mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", - "faucet_derivation_path": "m/44'/5757'/0'/0/0", - "bitcoin_controller_block_time": 0, - "bitcoin_controller_automining_disabled": true, - "disable_bitcoin_explorer": true, - "disable_stacks_explorer": true, + "namespace": "test-namespace", "disable_stacks_api": false, - "epoch_2_0": 100, - "epoch_2_05": 102, - "epoch_2_1": 106, - "epoch_2_2": 120, - "pox_2_activation": 112, - "pox_2_unlock_height": 112, - "project_manifest": { - "name": "px", - "description": "my description", - "authors": ["test1", "test2"], - "requirements": ["test1", "test2"] - }, - "accounts": [ - { - "name": "deployer", - "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", - "balance": 100000000000000, - "stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - { - "name": "wallet_1", - "mnemonic": "sell invite acquire kitten bamboo drastic jelly vivid peace spawn twice guilt pave pen trash pretty park cube fragile unaware remain midnight betray rebuild", - "balance": 100000000000000, - "stx_address": "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5" - } - ], "deployment_plan": { "id": 0, "name": "Devnet deployment", "network": "devnet", - "stacks-node": "http://localhost:20443", - "bitcoin-node": "http://px-devnet:px-devnet@localhost:18443", - "plan": { - "batches": [ - { - "id": 0, - "transactions": [ - { - "contract-publish": { - "contract-name": "px", - "expected-sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "cost": 18060, - "path": "contracts/px.clar", - "anchor-block-only": true, - "clarity-version": 2 - } + "stacks_node": "http://localhost:20443", + "bitcoin_node": "http://px-devnet:px-devnet@localhost:18443", + "genesis": null, + "batches": [ + { + "id": 0, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "KGRlZmluZS10cmFpdCBuZnQtdHJhaXQKICAoCiAgICA7OyBMYXN0IHRva2VuIElELCBsaW1pdGVkIHRvIHVpbnQgcmFuZ2UKICAgIChnZXQtbGFzdC10b2tlbi1pZCAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyBVUkkgZm9yIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgdG9rZW4KICAgIChnZXQtdG9rZW4tdXJpICh1aW50KSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctYXNjaWkgMjU2KSkgdWludCkpCgogICAgIDs7IE93bmVyIG9mIGEgZ2l2ZW4gdG9rZW4gaWRlbnRpZmllcgogICAgKGdldC1vd25lciAodWludCkgKHJlc3BvbnNlIChvcHRpb25hbCBwcmluY2lwYWwpIHVpbnQpKQoKICAgIDs7IFRyYW5zZmVyIGZyb20gdGhlIHNlbmRlciB0byBhIG5ldyBwcmluY2lwYWwKICAgICh0cmFuc2ZlciAodWludCBwcmluY2lwYWwgcHJpbmNpcGFsKSAocmVzcG9uc2UgYm9vbCB1aW50KSkKICApCik=", + "clarity_version": 1, + "cost": 4670, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.clar" } - ], - "epoch": "2.1" + } + ], + "epoch": "2.0" + }, + { + "id": 1, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "KGRlZmluZS10cmFpdCBzaXAtMDEwLXRyYWl0CiAgKAogICAgOzsgVHJhbnNmZXIgZnJvbSB0aGUgY2FsbGVyIHRvIGEgbmV3IHByaW5jaXBhbAogICAgKHRyYW5zZmVyICh1aW50IHByaW5jaXBhbCBwcmluY2lwYWwgKG9wdGlvbmFsIChidWZmIDM0KSkpIChyZXNwb25zZSBib29sIHVpbnQpKQoKICAgIDs7IHRoZSBodW1hbiByZWFkYWJsZSBuYW1lIG9mIHRoZSB0b2tlbgogICAgKGdldC1uYW1lICgpIChyZXNwb25zZSAoc3RyaW5nLWFzY2lpIDMyKSB1aW50KSkKCiAgICA7OyB0aGUgdGlja2VyIHN5bWJvbCwgb3IgZW1wdHkgaWYgbm9uZQogICAgKGdldC1zeW1ib2wgKCkgKHJlc3BvbnNlIChzdHJpbmctYXNjaWkgMzIpIHVpbnQpKQoKICAgIDs7IHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdXNlZCwgZS5nLiA2IHdvdWxkIG1lYW4gMV8wMDBfMDAwIHJlcHJlc2VudHMgMSB0b2tlbgogICAgKGdldC1kZWNpbWFscyAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyB0aGUgYmFsYW5jZSBvZiB0aGUgcGFzc2VkIHByaW5jaXBhbAogICAgKGdldC1iYWxhbmNlIChwcmluY2lwYWwpIChyZXNwb25zZSB1aW50IHVpbnQpKQoKICAgIDs7IHRoZSBjdXJyZW50IHRvdGFsIHN1cHBseSAod2hpY2ggZG9lcyBub3QgbmVlZCB0byBiZSBhIGNvbnN0YW50KQogICAgKGdldC10b3RhbC1zdXBwbHkgKCkgKHJlc3BvbnNlIHVpbnQgdWludCkpCgogICAgOzsgYW4gb3B0aW9uYWwgVVJJIHRoYXQgcmVwcmVzZW50cyBtZXRhZGF0YSBvZiB0aGlzIHRva2VuCiAgICAoZ2V0LXRva2VuLXVyaSAoKSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctdXRmOCAyNTYpKSB1aW50KSkKICApCik=", + "clarity_version": 1, + "cost": 8390, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.clar" + } + } + ], + "epoch": "2.05" + }, + { + "id": 2, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "OzsgSW4gb3JkZXIgdG8gc3VwcG9ydCB3aXRoZHJhd2luZyBhbiBhc3NldCB0aGF0IHdhcyBtaW50ZWQgb24gYSBzdWJuZXQsIHRoZQo7OyBMMSBjb250cmFjdCBtdXN0IGltcGxlbWVudCB0aGlzIHRyYWl0LgooZGVmaW5lLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQKICAoCiAgICA7OyBQcm9jZXNzIGEgd2l0aGRyYXdhbCBmcm9tIHRoZSBzdWJuZXQgZm9yIGFuIGFzc2V0IHdoaWNoIGRvZXMgbm90IHlldAogICAgOzsgZXhpc3Qgb24gdGhpcyBuZXR3b3JrLCBhbmQgdGh1cyByZXF1aXJlcyBhIG1pbnQuCiAgICAobWludC1mcm9tLXN1Ym5ldAogICAgICAoCiAgICAgICAgdWludCAgICAgICA7OyBhc3NldC1pZCAoTkZUKSBvciBhbW91bnQgKEZUKQogICAgICAgIHByaW5jaXBhbCAgOzsgc2VuZGVyCiAgICAgICAgcHJpbmNpcGFsICA7OyByZWNpcGllbnQKICAgICAgKQogICAgICAocmVzcG9uc2UgYm9vbCB1aW50KQogICAgKQogICkKKQ==", + "clarity_version": 2, + "cost": 4810, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1.clar" + } + }, + { + "transaction_type": "RequirementPublish", + "contract_id": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "OzsgVGhlIC5zdWJuZXQgY29udHJhY3QKCihkZWZpbmUtY29uc3RhbnQgQ09OVFJBQ1RfQUREUkVTUyAoYXMtY29udHJhY3QgdHgtc2VuZGVyKSkKCjs7IEVycm9yIGNvZGVzCihkZWZpbmUtY29uc3RhbnQgRVJSX0JMT0NLX0FMUkVBRFlfQ09NTUlUVEVEIDEpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUlORVIgMikKKGRlZmluZS1jb25zdGFudCBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQgMykKKGRlZmluZS1jb25zdGFudCBFUlJfVFJBTlNGRVJfRkFJTEVEIDQpCihkZWZpbmUtY29uc3RhbnQgRVJSX0RJU0FMTE9XRURfQVNTRVQgNSkKKGRlZmluZS1jb25zdGFudCBFUlJfQVNTRVRfQUxSRUFEWV9BTExPV0VEIDYpCihkZWZpbmUtY29uc3RhbnQgRVJSX01FUktMRV9ST09UX0RPRVNfTk9UX01BVENIIDcpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QgOCkKKGRlZmluZS1jb25zdGFudCBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCA5KQooZGVmaW5lLWNvbnN0YW50IEVSUl9WQUxJREFUSU9OX0ZBSUxFRCAxMCkKOzs7IFRoZSB2YWx1ZSBzdXBwbGllZCBmb3IgYHRhcmdldC1jaGFpbi10aXBgIGRvZXMgbm90IG1hdGNoIHRoZSBjdXJyZW50IGNoYWluIHRpcC4KKGRlZmluZS1jb25zdGFudCBFUlJfSU5WQUxJRF9DSEFJTl9USVAgMTEpCjs7OyBUaGUgY29udHJhY3Qgd2FzIGNhbGxlZCBiZWZvcmUgcmVhY2hpbmcgdGhpcy1jaGFpbiBoZWlnaHQgcmVhY2hlcyAxLgooZGVmaW5lLWNvbnN0YW50IEVSUl9DQUxMRURfVE9PX0VBUkxZIDEyKQooZGVmaW5lLWNvbnN0YW50IEVSUl9NSU5UX0ZBSUxFRCAxMykKKGRlZmluZS1jb25zdGFudCBFUlJfQVRURU1QVF9UT19UUkFOU0ZFUl9aRVJPX0FNT1VOVCAxNCkKKGRlZmluZS1jb25zdGFudCBFUlJfSU5fQ09NUFVUQVRJT04gMTUpCjs7IFRoZSBjb250cmFjdCBkb2VzIG5vdCBvd24gdGhpcyBORlQgdG8gd2l0aGRyYXcgaXQuCihkZWZpbmUtY29uc3RhbnQgRVJSX05GVF9OT1RfT1dORURfQllfQ09OVFJBQ1QgMTYpCihkZWZpbmUtY29uc3RhbnQgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQgMzApCgo7OyBNYXAgZnJvbSBTdGFja3MgYmxvY2sgaGVpZ2h0IHRvIGJsb2NrIGNvbW1pdAooZGVmaW5lLW1hcCBibG9jay1jb21taXRzIHVpbnQgKGJ1ZmYgMzIpKQo7OyBNYXAgcmVjb3JkaW5nIHdpdGhkcmF3YWwgcm9vdHMKKGRlZmluZS1tYXAgd2l0aGRyYXdhbC1yb290cy1tYXAgKGJ1ZmYgMzIpIGJvb2wpCjs7IE1hcCByZWNvcmRpbmcgcHJvY2Vzc2VkIHdpdGhkcmF3YWwgbGVhdmVzCihkZWZpbmUtbWFwIHByb2Nlc3NlZC13aXRoZHJhd2FsLWxlYXZlcy1tYXAgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogKGJ1ZmYgMzIpLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogKGJ1ZmYgMzIpIH0gYm9vbCkKCjs7IHByaW5jaXBhbCB0aGF0IGNhbiBjb21taXQgYmxvY2tzCihkZWZpbmUtZGF0YS12YXIgbWluZXIgcHJpbmNpcGFsIHR4LXNlbmRlcikKOzsgcHJpbmNpcGFsIHRoYXQgY2FuIHJlZ2lzdGVyIGNvbnRyYWN0cwooZGVmaW5lLWRhdGEtdmFyIGFkbWluIHByaW5jaXBhbCB0eC1zZW5kZXIpCgo7OyBNYXAgb2YgYWxsb3dlZCBjb250cmFjdHMgZm9yIGFzc2V0IHRyYW5zZmVycyAtIG1hcHMgTDEgY29udHJhY3QgcHJpbmNpcGFsIHRvIEwyIGNvbnRyYWN0IHByaW5jaXBhbAooZGVmaW5lLW1hcCBhbGxvd2VkLWNvbnRyYWN0cyBwcmluY2lwYWwgcHJpbmNpcGFsKQoKOzsgVXNlIHRyYWl0IGRlY2xhcmF0aW9ucwoodXNlLXRyYWl0IG5mdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQubmZ0LXRyYWl0Lm5mdC10cmFpdCkKKHVzZS10cmFpdCBmdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQuc2lwLTAxMC10cmFpdC1mdC1zdGFuZGFyZC5zaXAtMDEwLXRyYWl0KQoodXNlLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQgLnN1Ym5ldC10cmFpdHMtdjEubWludC1mcm9tLXN1Ym5ldC10cmFpdCkKCjs7IFVwZGF0ZSB0aGUgbWluZXIgZm9yIHRoaXMgY29udHJhY3QuCihkZWZpbmUtcHVibGljICh1cGRhdGUtbWluZXIgKG5ldy1taW5lciBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgKGFzc2VydHMhIChpcy1lcSB0eC1zZW5kZXIgKHZhci1nZXQgbWluZXIpKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKICAgICAgICAob2sgKHZhci1zZXQgbWluZXIgbmV3LW1pbmVyKSkKICAgICkKKQoKOzsgUmVnaXN0ZXIgYSBuZXcgRlQgY29udHJhY3QgdG8gYmUgc3VwcG9ydGVkIGJ5IHRoaXMgc3VibmV0LgooZGVmaW5lLXB1YmxpYyAocmVnaXN0ZXItbmV3LWZ0LWNvbnRyYWN0IChmdC1jb250cmFjdCA8ZnQtdHJhaXQ+KSAobDItY29udHJhY3QgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFZlcmlmeSB0aGF0IHR4LXNlbmRlciBpcyBhbiBhdXRob3JpemVkIGFkbWluCiAgICAgICAgKGFzc2VydHMhIChpcy1hZG1pbiB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBTZXQgdXAgdGhlIGFzc2V0cyB0aGF0IHRoZSBjb250cmFjdCBpcyBhbGxvd2VkIHRvIHRyYW5zZmVyCiAgICAgICAgKGFzc2VydHMhIChtYXAtaW5zZXJ0IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAiZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdDogKGNvbnRyYWN0LW9mIGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbDItY29udHJhY3Q6IGwyLWNvbnRyYWN0CiAgICAgICAgfSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IFJlZ2lzdGVyIGEgbmV3IE5GVCBjb250cmFjdCB0byBiZSBzdXBwb3J0ZWQgYnkgdGhpcyBzdWJuZXQuCihkZWZpbmUtcHVibGljIChyZWdpc3Rlci1uZXctbmZ0LWNvbnRyYWN0IChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pIChsMi1jb250cmFjdCBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgOzsgVmVyaWZ5IHRoYXQgdHgtc2VuZGVyIGlzIGFuIGF1dGhvcml6ZWQgYWRtaW4KICAgICAgICAoYXNzZXJ0cyEgKGlzLWFkbWluIHR4LXNlbmRlcikgKGVyciBFUlJfSU5WQUxJRF9NSU5FUikpCgogICAgICAgIDs7IFNldCB1cCB0aGUgYXNzZXRzIHRoYXQgdGhlIGNvbnRyYWN0IGlzIGFsbG93ZWQgdG8gdHJhbnNmZXIKICAgICAgICAoYXNzZXJ0cyEgKG1hcC1pbnNlcnQgYWxsb3dlZC1jb250cmFjdHMgKGNvbnRyYWN0LW9mIG5mdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAibmZ0IiwKICAgICAgICAgICAgbDEtY29udHJhY3Q6IChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpLAogICAgICAgICAgICBsMi1jb250cmFjdDogbDItY29udHJhY3QKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhIG1pbmVyCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGlzLW1pbmVyIChtaW5lci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIG1pbmVyLXRvLWNoZWNrICh2YXItZ2V0IG1pbmVyKSkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhbiBhZG1pbgo7OyBSZXR1cm5zIGJvb2wKKGRlZmluZS1wcml2YXRlIChpcy1hZG1pbiAoYWRkci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIGFkZHItdG8tY2hlY2sgKHZhci1nZXQgYWRtaW4pKQopCgo7OyBIZWxwZXIgZnVuY3Rpb246IGRldGVybWluZXMgd2hldGhlciB0aGUgY29tbWl0LWJsb2NrIG9wZXJhdGlvbiBzYXRpc2ZpZXMgcHJlLWNvbmRpdGlvbnMKOzsgbGlzdGVkIGluIGBjb21taXQtYmxvY2tgLgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChjYW4tY29tbWl0LWJsb2NrPyAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KSAgKHRhcmdldC1jaGFpbi10aXAgKGJ1ZmYgMzIpKSkKICAgIChiZWdpbgogICAgICAgIDs7IGNoZWNrIG5vIGJsb2NrIGhhcyBiZWVuIGNvbW1pdHRlZCBhdCB0aGlzIGhlaWdodAogICAgICAgIChhc3NlcnRzISAoaXMtbm9uZSAobWFwLWdldD8gYmxvY2stY29tbWl0cyBjb21taXQtYmxvY2staGVpZ2h0KSkgKGVyciBFUlJfQkxPQ0tfQUxSRUFEWV9DT01NSVRURUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IGB0YXJnZXQtY2hhaW4tdGlwYCBtYXRjaGVzIHRoZSBidXJuIGNoYWluIHRpcAogICAgICAgIChhc3NlcnRzISAoaXMtZXEKICAgICAgICAgICAgdGFyZ2V0LWNoYWluLXRpcAogICAgICAgICAgICAodW53cmFwISAoZ2V0LWJsb2NrLWluZm8/IGlkLWhlYWRlci1oYXNoICgtIGJsb2NrLWhlaWdodCB1MSkpIChlcnIgRVJSX0NBTExFRF9UT09fRUFSTFkpKSApCiAgICAgICAgICAgIChlcnIgRVJSX0lOVkFMSURfQ0hBSU5fVElQKSkKCiAgICAgICAgOzsgY2hlY2sgdGhhdCB0aGUgdHggc2VuZGVyIGlzIG9uZSBvZiB0aGUgbWluZXJzCiAgICAgICAgKGFzc2VydHMhIChpcy1taW5lciB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSBtaW5lciBjYWxsZWQgdGhpcyBjb250cmFjdCBkaXJlY3RseQogICAgICAgIChhc3NlcnRzISAoaXMtbWluZXIgY29udHJhY3QtY2FsbGVyKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEhlbHBlciBmdW5jdGlvbjogbW9kaWZpZXMgdGhlIGJsb2NrLWNvbW1pdHMgbWFwIHdpdGggYSBuZXcgY29tbWl0IGFuZCBwcmludHMgcmVsYXRlZCBpbmZvCjs7IFJldHVybnMgcmVzcG9uc2U8KGJ1ZmYgMzIpLCA/PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLWNvbW1pdC1ibG9jawogICAgICAgIChibG9jayAoYnVmZiAzMikpCiAgICAgICAgKGNvbW1pdC1ibG9jay1oZWlnaHQgdWludCkKICAgICAgICAodGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICApCiAgICAoYmVnaW4KICAgICAgICAobWFwLXNldCBibG9jay1jb21taXRzIGNvbW1pdC1ibG9jay1oZWlnaHQgYmxvY2spCiAgICAgICAgKG1hcC1zZXQgd2l0aGRyYXdhbC1yb290cy1tYXAgd2l0aGRyYXdhbC1yb290IHRydWUpCiAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgZXZlbnQ6ICJibG9jay1jb21taXQiLAogICAgICAgICAgICBibG9jay1jb21taXQ6IGJsb2NrLAogICAgICAgICAgICBibG9jay1oZWlnaHQ6IGNvbW1pdC1ibG9jay1oZWlnaHQsCiAgICAgICAgICAgIHdpdGhkcmF3YWwtcm9vdDogd2l0aGRyYXdhbC1yb290LAogICAgICAgICAgICB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQ6IHRhcmdldC1idXJuLWJsb2NrLWhlaWdodAogICAgICAgIH0pCiAgICAgICAgKG9rIGJsb2NrKQogICAgKQopCgo7OyBUaGUgc3VibmV0IG1pbmVyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gY29tbWl0IGEgYmxvY2sgYXQgYSBwYXJ0aWN1bGFyIGhlaWdodC4KOzsgYGJsb2NrYCBpcyB0aGUgaGFzaCBvZiB0aGUgYmxvY2sgYmVpbmcgc3VibWl0dGVkLgo7OyBgdGFyZ2V0LWNoYWluLXRpcGAgaXMgdGhlIGBpZC1oZWFkZXItaGFzaGAgb2YgdGhlIGJ1cm4gYmxvY2sgKGkuZS4sIGJsb2NrIG9uCjs7ICAgIHRoaXMgY2hhaW4pIHRoYXQgdGhlIG1pbmVyIGludGVuZHMgdG8gYnVpbGQgb2ZmLgo7Owo7OyBGYWlscyBpZjoKOzsgIDEpIHdlIGhhdmUgYWxyZWFkeSBjb21taXR0ZWQgYXQgdGhpcyBibG9jayBoZWlnaHQKOzsgIDIpIGB0YXJnZXQtY2hhaW4tdGlwYCBpcyBub3QgdGhlIGJ1cm4gY2hhaW4gdGlwIChpLmUuLCBvbiB0aGlzIGNoYWluKQo7OyAgMykgdGhlIHNlbmRlciBpcyBub3QgYSBtaW5lcgooZGVmaW5lLXB1YmxpYyAoY29tbWl0LWJsb2NrCiAgICAgICAgKGJsb2NrIChidWZmIDMyKSkKICAgICAgICAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KQogICAgICAgICh0YXJnZXQtY2hhaW4tdGlwIChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKCh0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgYmxvY2staGVpZ2h0KSkKICAgICAgICAodHJ5ISAoY2FuLWNvbW1pdC1ibG9jaz8gdGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHRhcmdldC1jaGFpbi10aXApKQogICAgICAgIChpbm5lci1jb21taXQtYmxvY2sgYmxvY2sgY29tbWl0LWJsb2NrLWhlaWdodCB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgd2l0aGRyYXdhbC1yb290KQogICAgKQopCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBORlQgQVNTRVQgVFJBTlNGRVJTCgo7OyBIZWxwZXIgZnVuY3Rpb24gdGhhdCB0cmFuc2ZlcnMgdGhlIHNwZWNpZmllZCBORlQgZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgdHJhbnNmZXIgaWQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChhcy1jb250cmFjdCAoY29udHJhY3QtY2FsbD8gbmZ0LW1pbnQtY29udHJhY3QgbWludC1mcm9tLXN1Ym5ldCBpZCBzZW5kZXIgcmVjaXBpZW50KSkpCiAgICAgICAgICAgIChtaW50LXJlc3VsdCAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICkKICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB0cmFuc2ZlciBzdWNjZWVkZWQKICAgICAgICAoYXNzZXJ0cyEgbWludC1yZXN1bHQgKGVyciBFUlJfTUlOVF9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikKICAgICAgICAoaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGNvbnRyYWN0LWNhbGw/IG5mdC1jb250cmFjdCBnZXQtb3duZXIgaWQpKQogICAgICAgICAgICAobmZ0LW93bmVyICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLW5mdCAoaXMtZXEgbmZ0LW93bmVyIChzb21lIENPTlRSQUNUX0FERFJFU1MpKSkKICAgICAgICAgICAgKG5vLW93bmVyIChpcy1lcSBuZnQtb3duZXIgbm9uZSkpCiAgICAgICAgKQoKICAgICAgICAoaWYgY29udHJhY3Qtb3ducy1uZnQKICAgICAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgIChpZiBuby1vd25lcgogICAgICAgICAgICAgICAgOzsgVHJ5IG1pbnRpbmcgdGhlIGFzc2V0IGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG93bmVyIG9mIHRoaXMgTkZUCiAgICAgICAgICAgICAgICAoaW5uZXItbWludC1uZnQtYXNzZXQgbmZ0LW1pbnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgICAgICA7OyBJbiB0aGlzIGNhc2UsIGEgcHJpbmNpcGFsIG90aGVyIHRoYW4gdGhpcyBjb250cmFjdCBvd25zIHRoaXMgTkZULCBzbyBtaW50aW5nIGlzIG5vdCBwb3NzaWJsZQogICAgICAgICAgICAgICAgKGVyciBFUlJfTUlOVF9GQUlMRUQpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgYW4gTkZUIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQoKICAgICAgICA7OyBUcnkgdG8gdHJhbnNmZXIgdGhlIE5GVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1uZnQtYXNzZXQgbmZ0LWNvbnRyYWN0IGlkIHNlbmRlciBDT05UUkFDVF9BRERSRVNTKSkgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgOzsgRW1pdCBhIHByaW50IGV2ZW50IC0gdGhlIG5vZGUgY29uc3VtZXMgdGhpcwogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1uZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IG5mdC1jb250cmFjdCksCiAgICAgICAgICAgIG5mdC1pZDogaWQsCiAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICBzdWJuZXQtY29udHJhY3QtaWQ6IHN1Ym5ldC1jb250cmFjdC1pZCwKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IEhlbHBlciBmdW5jdGlvbiBmb3IgYHdpdGhkcmF3LW5mdC1hc3NldGAKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChpbm5lci13aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChsMi1jb250cmFjdCBwcmluY2lwYWwpCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAobGV0ICgoaGFzaGVzLWFyZS12YWxpZCAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMgd2l0aGRyYXdhbC1yb290IHdpdGhkcmF3YWwtbGVhZi1oYXNoIHNpYmxpbmctaGFzaGVzKSkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISBoYXNoZXMtYXJlLXZhbGlkKSAoZXJyIEVSUl9WQUxJREFUSU9OX0ZBSUxFRCkpCgogICAgICAgIDs7IGNoZWNrIHRoYXQgdGhlIHdpdGhkcmF3YWwgcmVxdWVzdCBkYXRhIG1hdGNoZXMgdGhlIHN1cHBsaWVkIGxlYWYgaGFzaAogICAgICAgIChhc3NlcnRzISAoaXMtZXEgd2l0aGRyYXdhbC1sZWFmLWhhc2gKICAgICAgICAgICAgICAgICAgICAgICAgIChsZWFmLWhhc2gtd2l0aGRyYXctbmZ0IGwyLWNvbnRyYWN0IGlkIHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBuZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgICAgIG1pbnQtY29udHJhY3QgKGFzLWNvbnRyYWN0IChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgbWludC1jb250cmFjdCBpZCByZWNpcGllbnQpKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgcmVjaXBpZW50KSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKQogICAgICAgICkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIHdpdGhkcmF3IHRoZSBzcGVjaWZpZWQgTkZUIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChpZCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChuZnQtbWludC1jb250cmFjdCAob3B0aW9uYWwgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIGFzc2V0IGJlbG9uZ3MgdG8gdGhlIGFsbG93ZWQtY29udHJhY3RzIG1hcAogICAgICAgICAgICAobDItY29udHJhY3QgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgICAodHJ5ISAoaW5uZXItd2l0aGRyYXctbmZ0LWFzc2V0CiAgICAgICAgICAgICAgICBuZnQtY29udHJhY3QKICAgICAgICAgICAgICAgIGwyLWNvbnRyYWN0CiAgICAgICAgICAgICAgICBpZAogICAgICAgICAgICAgICAgcmVjaXBpZW50CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWlkCiAgICAgICAgICAgICAgICBoZWlnaHQKICAgICAgICAgICAgICAgIG5mdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLXJvb3QKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICBzaWJsaW5nLWhhc2hlcwogICAgICAgICAgICApKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgewogICAgICAgICAgICBldmVudDogIndpdGhkcmF3LW5mdCIsCiAgICAgICAgICAgIGwxLWNvbnRyYWN0LWlkOiAoYXMtY29udHJhY3QgbmZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbmZ0LWlkOiBpZCwKICAgICAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IExpa2UgYGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtbmZ0LWFzc2V0IGJ1dCB3aXRob3V0IGFsbG93aW5nIG9yIHJlcXVpcmluZyBhIG1pbnQgZnVuY3Rpb24uIEluIG9yZGVyIHRvIHdpdGhkcmF3LCB0aGUgdXNlciBtdXN0Cjs7IGhhdmUgdGhlIGFwcHJvcHJpYXRlIGJhbGFuY2UuCihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgZ2V0LW93bmVyIGlkKSkKICAgICAgICAgICAgKG5mdC1vd25lciAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICAoY29udHJhY3Qtb3ducy1uZnQgKGlzLWVxIG5mdC1vd25lciAoc29tZSBDT05UUkFDVF9BRERSRVNTKSkpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEgY29udHJhY3Qtb3ducy1uZnQgKGVyciBFUlJfTkZUX05PVF9PV05FRF9CWV9DT05UUkFDVCkpCiAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICApCikKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgRk9SIEZVTkdJQkxFIFRPS0VOIEFTU0VUIFRSQU5TRkVSUwoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aGUgZnVuZ2libGUgdG9rZW4gZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChzZW5kZXIgcHJpbmNpcGFsKQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgdHJhbnNmZXIgYW1vdW50IHNlbmRlciByZWNpcGllbnQgbWVtbykpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgRklYTUU6IFNJUC0wMTAgZG9lc24ndCByZXF1aXJlIHRoYXQgdHJhbnNmZXIgcmV0dXJucyAob2sgdHJ1ZSkgb24gc3VjY2Vzcywgc28gaXMgdGhpcyBjaGVjayBuZWNlc3Nhcnk/CiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LWZ0LWFzc2V0CiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAoc2VuZGVyIHByaW5jaXBhbCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGFzLWNvbnRyYWN0IChjb250cmFjdC1jYWxsPyBmdC1taW50LWNvbnRyYWN0IG1pbnQtZnJvbS1zdWJuZXQgYW1vdW50IHNlbmRlciByZWNpcGllbnQpKSkKICAgICAgICAgICAgKG1pbnQtcmVzdWx0ICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISBtaW50LXJlc3VsdCAoZXJyIEVSUl9NSU5UX0ZBSUxFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgZ2V0LWJhbGFuY2UgQ09OVFJBQ1RfQUREUkVTUykpCiAgICAgICAgICAgIChjb250cmFjdC1mdC1iYWxhbmNlICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLWVub3VnaCAoPj0gY29udHJhY3QtZnQtYmFsYW5jZSBhbW91bnQpKQogICAgICAgICAgICAoYW1vdW50LXRvLXRyYW5zZmVyIChpZiBjb250cmFjdC1vd25zLWVub3VnaCBhbW91bnQgY29udHJhY3QtZnQtYmFsYW5jZSkpCiAgICAgICAgICAgIChhbW91bnQtdG8tbWludCAoLSBhbW91bnQgYW1vdW50LXRvLXRyYW5zZmVyKSkKICAgICAgICApCgogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRvdGFsIGJhbGFuY2UgYmV0d2VlbiB0aGUgdHJhbnNmZXIgYW5kIG1pbnQgaXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIGJhbGFuY2UKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIGFtb3VudCAoKyBhbW91bnQtdG8tdHJhbnNmZXIgYW1vdW50LXRvLW1pbnQpKSAoZXJyIEVSUl9JTl9DT01QVVRBVElPTikpCgogICAgICAgIChhbmQKICAgICAgICAgICAgKD4gYW1vdW50LXRvLXRyYW5zZmVyIHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50LXRvLXRyYW5zZmVyIENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICkKICAgICAgICAoYW5kCiAgICAgICAgICAgICg+IGFtb3VudC10by1taW50IHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItbWludC1mdC1hc3NldCBmdC1taW50LWNvbnRyYWN0IGFtb3VudC10by1taW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50KSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FsbHMgdGhpcyBmdW5jdGlvbiB0byBkZXBvc2l0IGEgZnVuZ2libGUgdG9rZW4gaW50byB0aGUgY29udHJhY3QuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKGRlcG9zaXQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKSkKICAgICAgICApCiAgICAgICAgOzsgVHJ5IHRvIHRyYW5zZmVyIHRoZSBGVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1mdC1hc3NldCBmdC1jb250cmFjdCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MgbWVtbykpIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkpCgogICAgICAgIChsZXQgKAogICAgICAgICAgICAgICAgKGZ0LW5hbWUgKHVud3JhcCEgKGNvbnRyYWN0LWNhbGw/IGZ0LWNvbnRyYWN0IGdldC1uYW1lKSAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICApCiAgICAgICAgICAgIDs7IEVtaXQgYSBwcmludCBldmVudCAtIHRoZSBub2RlIGNvbnN1bWVzIHRoaXMKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1mdCIsCiAgICAgICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgICAgIGZ0LW5hbWU6IGZ0LW5hbWUsCiAgICAgICAgICAgICAgICBmdC1hbW91bnQ6IGFtb3VudCwKICAgICAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICAgICAgc3VibmV0LWNvbnRyYWN0LWlkOiBzdWJuZXQtY29udHJhY3QtaWQsCiAgICAgICAgICAgIH0pCiAgICAgICAgKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBwZXJmb3JtcyB2YWxpZGl0eSBjaGVja3MgcmVsYXRlZCB0byB0aGUgd2l0aGRyYXdhbCBhbmQgcGVyZm9ybXMgdGhlIHdpdGhkcmF3YWwgYXMgd2VsbC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItd2l0aGRyYXctZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgKG9wdGlvbmFsIDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgaGFzaGVzLWFyZS12YWxpZCkgKGVyciBFUlJfVkFMSURBVElPTl9GQUlMRUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LWZ0IChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgYW1vdW50IHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBmdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICAgICAgbWludC1jb250cmFjdCAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQgZnQtY29udHJhY3QgbWludC1jb250cmFjdCBhbW91bnQgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICAgICAgICAgIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkKICAgICAgICApCgogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgKGVyciBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB3aXRoZHJhdyBzb21lIGFtb3VudCBvZiBhIGZ1bmdpYmxlIHRva2VuIGFzc2V0IGZyb20gdGhlCjs7IGNvbnRyYWN0IGFuZCBzZW5kIGl0IHRvIGEgcmVjaXBpZW50Lgo7OyBJbiBvcmRlciBmb3IgdGhpcyB3aXRoZHJhd2FsIHRvIGdvIHRocm91Z2gsIHRoZSBnaXZlbiB3aXRoZHJhd2FsIG11c3QgaGF2ZSBiZWVuIGluY2x1ZGVkCjs7IGluIGEgd2l0aGRyYXdhbCBNZXJrbGUgdHJlZSBhIHN1Ym5ldCBtaW5lciBzdWJtaXR0ZWQuIFRoZSB1c2VyIG11c3QgcHJvdmlkZSB0aGUgbGVhZgo7OyBoYXNoIG9mIHRoZWlyIHdpdGhkcmF3YWwgYW5kIHRoZSByb290IGhhc2ggb2YgdGhlIHNwZWNpZmljIE1lcmtsZSB0cmVlIHRoZWlyIHdpdGhkcmF3YWwKOzsgaXMgaW5jbHVkZWQgaW4uIFRoZXkgbXVzdCBhbHNvIHByb3ZpZGUgYSBsaXN0IG9mIHNpYmxpbmcgaGFzaGVzLiBUaGUgd2l0aGRyYXcgZnVuY3Rpb24KOzsgdXNlcyB0aGUgcHJvdmlkZWQgaGFzaGVzIHRvIGVuc3VyZSB0aGUgcmVxdWVzdGVkIHdpdGhkcmF3YWwgaXMgdmFsaWQuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKHdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgICAgIChmdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAoYmVnaW4KICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB3aXRoZHJhdyBhbW91bnQgaXMgcG9zaXRpdmUKICAgICAgICAoYXNzZXJ0cyEgKD4gYW1vdW50IHUwKSAoZXJyIEVSUl9BVFRFTVBUX1RPX1RSQU5TRkVSX1pFUk9fQU1PVU5UKSkKCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEgKGlubmVyLXdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgICAgICAgICBmdC1jb250cmFjdAogICAgICAgICAgICAgICAgYW1vdW50CiAgICAgICAgICAgICAgICByZWNpcGllbnQKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtaWQKICAgICAgICAgICAgICAgIGhlaWdodAogICAgICAgICAgICAgICAgbWVtbwogICAgICAgICAgICAgICAgZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWxlYWYtaGFzaAogICAgICAgICAgICAgICAgc2libGluZy1oYXNoZXMpKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAobGV0ICgKICAgICAgICAgICAgICAgIChmdC1uYW1lICh1bndyYXAhIChjb250cmFjdC1jYWxsPyBmdC1jb250cmFjdCBnZXQtbmFtZSkgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICAgICAgKQogICAgICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAid2l0aGRyYXctZnQiLAogICAgICAgICAgICAgICAgbDEtY29udHJhY3QtaWQ6IChhcy1jb250cmFjdCBmdC1jb250cmFjdCksCiAgICAgICAgICAgICAgICBmdC1uYW1lOiBmdC1uYW1lLAogICAgICAgICAgICAgICAgZnQtYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgfSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgoKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBTVFggVFJBTlNGRVJTCgoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIHRoZSBnaXZlbiBhbW91bnQgZnJvbSB0aGUgc3BlY2lmaWVkIGZ1bmdpYmxlIHRva2VuIGZyb20gdGhlIGdpdmVuIHNlbmRlciB0byB0aGUgZ2l2ZW4gcmVjaXBpZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1zdHggKGFtb3VudCB1aW50KSAoc2VuZGVyIHByaW5jaXBhbCkgKHJlY2lwaWVudCBwcmluY2lwYWwpKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoc3R4LXRyYW5zZmVyPyBhbW91bnQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISB0cmFuc2Zlci1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgU1RYIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LXN0eCAoYW1vdW50IHVpbnQpIChzZW5kZXIgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFRyeSB0byB0cmFuc2ZlciB0aGUgU1RYIHRvIHRoaXMgY29udHJhY3QKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQgLSB0aGUgbm9kZSBjb25zdW1lcyB0aGlzCiAgICAgICAgKHByaW50IHsgZXZlbnQ6ICJkZXBvc2l0LXN0eCIsIHNlbmRlcjogc2VuZGVyLCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICApCiAgICAoc2hhNTEyLzI1NiAoY29uY2F0IDB4MDAgKHVud3JhcC1wYW5pYyAodG8tY29uc2Vuc3VzLWJ1ZmY/CiAgICAgICAgewogICAgICAgICAgICB0eXBlOiAic3R4IiwKICAgICAgICAgICAgYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1uZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChuZnQtaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJuZnQiLAogICAgICAgICAgICBuZnQtaWQ6IG5mdC1pZCwKICAgICAgICAgICAgYXNzZXQtY29udHJhY3Q6IGFzc2V0LWNvbnRyYWN0LAogICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgd2l0aGRyYXdhbC1pZDogd2l0aGRyYXdhbC1pZCwKICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQKICAgICAgICB9KSkpCiAgICApCikKCihkZWZpbmUtcmVhZC1vbmx5IChsZWFmLWhhc2gtd2l0aGRyYXctZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJmdCIsCiAgICAgICAgICAgIGFtb3VudDogYW1vdW50LAogICAgICAgICAgICBhc3NldC1jb250cmFjdDogYXNzZXQtY29udHJhY3QsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKOzsgQSB1c2VyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gd2l0aGRyYXcgU1RYIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKCiAgICAgICAgKGFzc2VydHMhICh0cnkhIGhhc2hlcy1hcmUtdmFsaWQpIChlcnIgRVJSX1ZBTElEQVRJT05fRkFJTEVEKSkKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LXN0eCBhbW91bnQgcmVjaXBpZW50IHdpdGhkcmF3YWwtaWQgaGVpZ2h0KSkKICAgICAgICAgICAgICAgICAgKGVyciBFUlJfVkFMSURBVElPTl9MRUFGX0ZBSUxFRCkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgdHgtc2VuZGVyIHJlY2lwaWVudCkpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgIChmaW5pc2gtd2l0aGRyYXcgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogd2l0aGRyYXdhbC1sZWFmLWhhc2gsIHdpdGhkcmF3YWwtcm9vdC1oYXNoOiB3aXRoZHJhd2FsLXJvb3QgfSkKICAgICAgICAgIChlcnIgRVJSX1dJVEhEUkFXQUxfQUxSRUFEWV9QUk9DRVNTRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgeyBldmVudDogIndpdGhkcmF3LXN0eCIsIHJlY2lwaWVudDogcmVjaXBpZW50LCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBHRU5FUkFMIFdJVEhEUkFXQUwgRlVOQ1RJT05TCgo7OyBUaGlzIGZ1bmN0aW9uIGNvbmNhdHMgdGhlIHR3byBnaXZlbiBoYXNoZXMgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuIEl0IGFsc28gcHJlcGVuZHMgdGhlIGJ1ZmYgYDB4MDFgLCB3aGljaCBpcwo7OyBhIHRhZyBkZW5vdGluZyBhIG5vZGUgKHZlcnN1cyBhIGxlYWYpLgo7OyBSZXR1cm5zIGEgYnVmZgooZGVmaW5lLXByaXZhdGUgKGNyZWF0ZS1ub2RlLWhhc2gKICAgICAgICAoY3Vyci1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoIChidWZmIDMyKSkKICAgICAgICAoaXMtc2libGluZy1sZWZ0LXNpZGUgYm9vbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY29uY2F0dGVkLWhhc2ggKGlmIGlzLXNpYmxpbmctbGVmdC1zaWRlCiAgICAgICAgICAgICAgICAgICAgKGNvbmNhdCBzaWJsaW5nLWhhc2ggY3Vyci1oYXNoKQogICAgICAgICAgICAgICAgICAgIChjb25jYXQgY3Vyci1oYXNoIHNpYmxpbmctaGFzaCkKICAgICAgICAgICAgICAgICkpCiAgICAgICAgICApCgogICAgICAgICAgKGNvbmNhdCAweDAxIGNvbmNhdHRlZC1oYXNoKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIGhhc2hlcyB0aGUgY3VyciBoYXNoIHdpdGggaXRzIHNpYmxpbmcgaGFzaC4KOzsgUmV0dXJucyAoYnVmZiAzMikKKGRlZmluZS1wcml2YXRlIChoYXNoLWhlbHAKICAgICAgICAoc2libGluZyB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pCiAgICAgICAgKGN1cnItbm9kZS1oYXNoIChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoc2libGluZy1oYXNoIChnZXQgaGFzaCBzaWJsaW5nKSkKICAgICAgICAgICAgKGlzLXNpYmxpbmctbGVmdC1zaWRlIChnZXQgaXMtbGVmdC1zaWRlIHNpYmxpbmcpKQogICAgICAgICAgICAobmV3LWJ1ZmYgKGNyZWF0ZS1ub2RlLWhhc2ggY3Vyci1ub2RlLWhhc2ggc2libGluZy1oYXNoIGlzLXNpYmxpbmctbGVmdC1zaWRlKSkKICAgICAgICApCiAgICAgICAoc2hhNTEyLzI1NiBuZXctYnVmZikKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBjaGVja3M6Cjs7ICAtIFRoYXQgdGhlIHByb3ZpZGVkIHdpdGhkcmF3YWwgcm9vdCBtYXRjaGVzIGEgcHJldmlvdXNseSBzdWJtaXR0ZWQgb25lIChwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uIGBjb21taXQtYmxvY2tgKQo7OyAgLSBUaGF0IHRoZSBjb21wdXRlZCB3aXRoZHJhd2FsIHJvb3QgbWF0Y2hlcyBhIHByZXZpb3VzIHZhbGlkIHdpdGhkcmF3YWwgcm9vdAo7OyAgLSBUaGF0IHRoZSBnaXZlbiB3aXRoZHJhd2FsIGxlYWYgaGFzaCBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBwcm9jZXNzZWQKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGJlZ2luCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdXNlciBzdWJtaXR0ZWQgYSB2YWxpZCB3aXRoZHJhd2FsIHJvb3QKICAgICAgICAoYXNzZXJ0cyEgKGlzLXNvbWUgKG1hcC1nZXQ/IHdpdGhkcmF3YWwtcm9vdHMtbWFwIHdpdGhkcmF3YWwtcm9vdCkpIChlcnIgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QpKQoKICAgICAgICA7OyBDaGVjayB0aGF0IHRoaXMgd2l0aGRyYXdhbCBsZWFmIGhhcyBub3QgYmVlbiBwcm9jZXNzZWQgYmVmb3JlCiAgICAgICAgKGFzc2VydHMhCiAgICAgICAgICAgIChpcy1ub25lCiAgICAgICAgICAgICAobWFwLWdldD8gcHJvY2Vzc2VkLXdpdGhkcmF3YWwtbGVhdmVzLW1hcAogICAgICAgICAgICAgICAgICAgICAgIHsgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IHdpdGhkcmF3YWwtbGVhZi1oYXNoLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogd2l0aGRyYXdhbC1yb290IH0pKQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKSkKCiAgICAgICAgKGxldCAoKGNhbGN1bGF0ZWQtd2l0aGRyYXdhbC1yb290IChmb2xkIGhhc2gtaGVscCBzaWJsaW5nLWhhc2hlcyB3aXRoZHJhd2FsLWxlYWYtaGFzaCkpCiAgICAgICAgICAgICAgKHJvb3RzLW1hdGNoIChpcy1lcSBjYWxjdWxhdGVkLXdpdGhkcmF3YWwtcm9vdCB3aXRoZHJhd2FsLXJvb3QpKSkKICAgICAgICAgICAgIChpZiByb290cy1tYXRjaAogICAgICAgICAgICAgICAgKG9rIHRydWUpCiAgICAgICAgICAgICAgICAoZXJyIEVSUl9NRVJLTEVfUk9PVF9ET0VTX05PVF9NQVRDSCkpCiAgICAgICAgKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBjYWxsZWQgYWZ0ZXIgdGhlIGFzc2V0IGluIHF1ZXN0aW9uIGhhcyBiZWVuIHRyYW5zZmVycmVkLgo7OyBJdCBhZGRzIHRoZSB3aXRoZHJhd2FsIGxlYWYgaGFzaCB0byBhIG1hcCBvZiBwcm9jZXNzZWQgbGVhdmVzLiBUaGlzIGVuc3VyZXMgdGhhdAo7OyB0aGlzIHdpdGhkcmF3YWwgbGVhZiBjYW4ndCBiZSB1c2VkIGFnYWluIHRvIHdpdGhkcmF3IGFkZGl0aW9uYWwgZnVuZHMuCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGZpbmlzaC13aXRoZHJhdwogICAgICAgICh3aXRoZHJhdy1pbmZvIHsKICAgICAgICAgICAgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290LWhhc2g6IChidWZmIDMyKQogICAgICAgIH0pCiAgICApCiAgICAobWFwLWluc2VydCBwcm9jZXNzZWQtd2l0aGRyYXdhbC1sZWF2ZXMtbWFwIHdpdGhkcmF3LWluZm8gdHJ1ZSkKKQo=", + "clarity_version": 2, + "cost": 290960, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2.clar" + } + }, + { + "transaction_type": "ContractPublish", + "contract_name": "px", + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "location": { + "path": "contracts/px.clar" + }, + "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", + "clarity_version": 2, + "cost": 18060, + "anchor_block_only": true + } + ], + "epoch": "2.1" + }, + { + "id": 3, + "transactions": [ + { + "transaction_type": "ContractCall", + "contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px", + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "method": "set-value-at", + "parameters": ["u0", "0xfffffa"], + "cost": 2240, + "anchor_block_only": false + }, + { + "transaction_type": "StxTransfer", + "expected_sender": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", + "recipient": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", + "mstx_amount": 200, + "memo": "0xabcabcabcaba00000000000000000000000000000000000000000000000000000000", + "cost": 2240, + "anchor_block_only": true + } + ], + "epoch": "2.1" + } + ], + "contracts": [ + { + "contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px", + "path": "/Users/micaiahreid/work/stx-px/contracts/px.clar", + "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK" + } + ] + }, + "network_manifest": { + "network": { + "name": "devnet", + "stacks_node_rpc_address": null, + "bitcoin_node_rpc_address": null, + "deployment_fee_rate": 10, + "sats_per_bytes": 10 + }, + "accounts": [ + { + "label": "deployer", + "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "btc_address": "mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH", + "is_mainnet": false + }, + { + "label": "faucet", + "mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6", + "btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", + "is_mainnet": false + }, + { + "label": "wallet_1", + "mnemonic": "crazy vibrant runway diagram beach language above aerobic maze coral this gas mirror output vehicle cover usage ecology unfold room feel file rocket expire", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "STB8E0SMACY4A6DCCH4WE48YGX3P877407QW176V", + "btc_address": "mha4u7F3e93P9Xy1WQgVvGtYtynnJtT22x", + "is_mainnet": false + }, + { + "label": "wallet_2", + "mnemonic": "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG", + "btc_address": "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG", + "is_mainnet": false + }, + { + "label": "wallet_3", + "mnemonic": "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC", + "btc_address": "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7", + "is_mainnet": false + }, + { + "label": "wallet_4", + "mnemonic": "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND", + "btc_address": "mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8", + "is_mainnet": false + }, + { + "label": "wallet_5", + "mnemonic": "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", + "btc_address": "mweN5WVqadScHdA81aATSdcVr4B6dNokqx", + "is_mainnet": false + }, + { + "label": "wallet_6", + "mnemonic": "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", + "btc_address": "mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt", + "is_mainnet": false + }, + { + "label": "wallet_7", + "mnemonic": "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3PF13W7Z0RRM42A8VZRVFQ75SV1K26RXEP8YGKJ", + "btc_address": "n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7", + "is_mainnet": false + }, + { + "label": "wallet_8", + "mnemonic": "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune", + "derivation": "m/44'/5757'/0'/0/0", + "balance": 100000000000000, + "stx_address": "ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP", + "btc_address": "n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw", + "is_mainnet": false + } + ], + "devnet_settings": { + "name": "devnet", + "network_id": null, + "orchestrator_ingestion_port": 20445, + "orchestrator_control_port": 20446, + "bitcoin_node_p2p_port": 18444, + "bitcoin_node_rpc_port": 18443, + "bitcoin_node_username": "devnet", + "bitcoin_node_password": "devnet", + "stacks_node_p2p_port": 20444, + "stacks_node_rpc_port": 20443, + "stacks_node_wait_time_for_microblocks": 50, + "stacks_node_first_attempt_time_ms": 500, + "stacks_node_subsequent_attempt_time_ms": 1000, + "stacks_node_events_observers": ["host.docker.internal:20455"], + "stacks_node_env_vars": [], + "stacks_api_port": 3999, + "stacks_api_events_port": 3700, + "stacks_api_env_vars": [], + "stacks_explorer_port": 8000, + "stacks_explorer_env_vars": [], + "bitcoin_explorer_port": 8001, + "bitcoin_controller_block_time": 60000, + "bitcoin_controller_automining_disabled": false, + "miner_stx_address": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", + "miner_secret_key_hex": "3b68e410cc7f9b8bae76f2f2991b69ecd0627c95da22a904065dfb2a73d0585f01", + "miner_btc_address": "n3GRiDLKWuKLCw1DZmV75W1mE35qmW2tQm", + "miner_mnemonic": "fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce", + "miner_derivation_path": "m/44'/5757'/0'/0/0", + "miner_coinbase_recipient": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", + "faucet_stx_address": "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6", + "faucet_secret_key_hex": "de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801", + "faucet_btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", + "faucet_mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", + "faucet_derivation_path": "m/44'/5757'/0'/0/0", + "working_dir": "/Users/micaiahreid/work/stx-px/tmp", + "postgres_port": 5432, + "postgres_username": "postgres", + "postgres_password": "postgres", + "stacks_api_postgres_database": "stacks_api", + "subnet_api_postgres_database": "subnet_api", + "pox_stacking_orders": [ + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_1", + "slots": 2, + "btc_address": "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC" + }, + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_2", + "slots": 1, + "btc_address": "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG" + }, + { + "start_at_cycle": 3, + "duration": 12, + "wallet": "wallet_3", + "slots": 1, + "btc_address": "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7" } - ] + ], + "execute_script": [], + "bitcoin_node_image_url": "quay.io/hirosystems/bitcoind:devnet-v3", + "stacks_node_image_url": "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0", + "stacks_api_image_url": "hirosystems/stacks-blockchain-api:latest", + "stacks_explorer_image_url": "hirosystems/explorer:latest", + "postgres_image_url": "postgres:14", + "bitcoin_explorer_image_url": "quay.io/hirosystems/bitcoin-explorer:devnet", + "disable_bitcoin_explorer": true, + "disable_stacks_explorer": true, + "disable_stacks_api": false, + "bind_containers_volumes": false, + "enable_subnet_node": false, + "subnet_node_image_url": "hirosystems/stacks-subnets:0.8.1", + "subnet_leader_stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "subnet_leader_secret_key_hex": "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", + "subnet_leader_btc_address": "mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH", + "subnet_leader_mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", + "subnet_leader_derivation_path": "m/44'/5757'/0'/0/0", + "subnet_node_p2p_port": 30444, + "subnet_node_rpc_port": 30443, + "subnet_events_ingestion_port": 30445, + "subnet_node_events_observers": [], + "subnet_contract_id": "ST173JK7NZBA4BS05ZRATQH1K89YJMTGEH1Z5J52E.subnet-v3-0-1", + "remapped_subnet_contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet-v3-0-1", + "subnet_node_env_vars": [], + "subnet_api_image_url": "hirosystems/stacks-blockchain-api:latest", + "subnet_api_port": 13999, + "subnet_api_events_port": 13700, + "subnet_api_env_vars": [], + "disable_subnet_api": true, + "docker_host": "unix:///var/run/docker.sock", + "components_host": "127.0.0.1", + "epoch_2_0": 100, + "epoch_2_05": 100, + "epoch_2_1": 101, + "epoch_2_2": 103, + "epoch_2_3": 104, + "epoch_2_4": 105, + "pox_2_activation": 102, + "use_docker_gateway_routing": false, + "docker_platform": "linux/amd64" } }, - "contracts": [ - { + "project_manifest": { + "metadata": { "name": "px", - "source": "XG47OyB0aXRsZTogcHhcbjs7IHZlcnNpb246XG47OyBzdW1tYXJ5OlxuOzsgZGVzY3JpcHRpb246IEFsbG93cyB1c2VycyB0byBwYXkgdG8gdXBkYXRlIGRhdGEgaW4gYSBtYXRyaXguIFxuOzsgIEVhY2ggbWF0cml4IHZhbHVlIG11c3QgYmUgYSBoZXhhZGVjaW1hbCB2YWx1ZSBmcm9tIDB4MDAwMDAwIHRvIDB4ZmZmZmZmLCByZXByZXNlbnRpbmcgYSBjb2xvciB0byBiZSBkaXNwbGF5ZWQgb24gYSBncmlkIGluIGEgd2ViIHBhZ2UuIFxuOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuXG5cbjs7IHRyYWl0c1xuOztcblxuOzsgdG9rZW4gZGVmaW5pdGlvbnNcbjs7IFxuXG47OyBjb25zdGFudHNcbjs7XG4oZGVmaW5lLWNvbnN0YW50IE1BWF9MT0MgdTEwMClcbihkZWZpbmUtY29uc3RhbnQgTUFYX1ZBTCAweGZmZmZmZilcbihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMClcbihkZWZpbmUtY29uc3RhbnQgQUxMX0xPQ1MgKGxpc3QgdTAgdTEgdTIgdTMgdTQgdTUgdTYgdTcgdTggdTkgdTEwIHUxMSB1MTIgdTEzIHUxNCB1MTUgdTE2IHUxNyB1MTggdTE5IHUyMCB1MjEgdTIyIHUyMyB1MjQgdTI1IHUyNiB1MjcgdTI4IHUyOSB1MzAgdTMxIHUzMiB1MzMgdTM0IHUzNSB1MzYgdTM3IHUzOCB1MzkgdTQwIHU0MSB1NDIgdTQzIHU0NCB1NDUgdTQ2IHU0NyB1NDggdTQ5IHU1MCB1NTEgdTUyIHU1MyB1NTQgdTU1IHU1NiB1NTcgdTU4IHU1OSB1NjAgdTYxIHU2MiB1NjMgdTY0IHU2NSB1NjYgdTY3IHU2OCB1NjkgdTcwIHU3MSB1NzIgdTczIHU3NCB1NzUgdTc2IHU3NyB1NzggdTc5IHU4MCB1ODEgdTgyIHU4MyB1ODQgdTg1IHU4NiB1ODcgdTg4IHU4OSB1OTAgdTkxIHU5MiB1OTMgdTk0IHU5NSB1OTYgdTk3IHU5OCB1OTkpKVxuOzsgZGF0YSB2YXJzXG47O1xuXG47OyBkYXRhIG1hcHNcbjs7XG4oZGVmaW5lLW1hcCBwaXhlbHMgdWludCAoYnVmZiAzKSlcblxuOzsgcHVibGljIGZ1bmN0aW9uc1xuOztcbihkZWZpbmUtcHVibGljIChzZXQtdmFsdWUtYXQgKGxvYyB1aW50KSAodmFsdWUgKGJ1ZmYgMykpKSBcbiAgICAoYmVnaW4gXG4gICAgICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgICAgICAoZXJyIFwiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy5cIilcbiAgICAgICAgICAgIChpZiAoPiB2YWx1ZSBNQVhfVkFMKVxuICAgICAgICAgICAgICAgIChlcnIgXCJWYWx1ZSBtdXN0IGJlIGxlc3MgdGhhbiAweGZmZmZmZi5cIilcbiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTClcbiAgICAgICAgICAgICAgICAgICAgKGVyciBcIlZhbHVlIG11c3QgYmUgZ3JlYXRlciB0aGFuIDB4MDAwMDAwLlwiKVxuICAgICAgICAgICAgICAgICAgICAob2sgKG1hcC1zZXQgcGl4ZWxzIGxvYyB2YWx1ZSkpXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKVxuICAgICAgICApXG4gICAgKVxuKVxuOzsgcmVhZCBvbmx5IGZ1bmN0aW9uc1xuOztcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdldC12YWx1ZS1hdCAobG9jIHVpbnQpKVxuICAgIChpZiAoPj0gbG9jIE1BWF9MT0MpXG4gICAgICAgIChlcnIgXCJPdXQgb2YgYm91bmRzLlwiKVxuICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSlcbiAgICApXG4pXG5cbihkZWZpbmUtcmVhZC1vbmx5IChnZXQtYWxsKSBcbiAgICAobWFwIGdldC12YWx1ZS1hdCBBTExfTE9DUylcbilcblxuKGRlZmluZS1yZWFkLW9ubHkgKGdlbmVzaXMtdGltZSAoaGVpZ2h0IHVpbnQpKVxuICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpXG4pXG47OyBwcml2YXRlIGZ1bmN0aW9uc1xuOztcbg==", - "clarity_version": 2, - "epoch": 2.1, - "deployer": null + "description": "my description", + "authors": ["test1", "test2"], + "telemetry": false, + "cache_dir": ".cache", + "requirements": [] + }, + "contracts": { + "px": { + "path": "contracts/px.clar", + "clarity_version": 2, + "epoch": 2.1 + } + }, + "repl": { + "analysis": { + "passes": [], + "check_checker": { + "strict": false, + "trusted_sender": false, + "trusted_caller": false, + "callee_filter": false + } + } } - ] + } } diff --git a/templates/bitcoind-chain-coordinator-pod.template.yaml b/templates/bitcoind-chain-coordinator-pod.template.yaml index 718a149..0d5d020 100644 --- a/templates/bitcoind-chain-coordinator-pod.template.yaml +++ b/templates/bitcoind-chain-coordinator-pod.template.yaml @@ -30,6 +30,7 @@ spec: - ./stacks-network - --namespace=$(NAMESPACE) - --manifest-path=/etc/stacks-network/project/Clarinet.toml + - --network-manifest-path=/etc/stacks-network/project/settings/Devnet.toml - --deployment-plan-path=/etc/stacks-network/project/deployments/default.devnet-plan.yaml - --project-root-path=/etc/stacks-network/project/ env: @@ -38,7 +39,7 @@ spec: configMapKeyRef: name: namespace-conf key: NAMESPACE - image: quay.io/hirosystems/stacks-network-orchestrator:latest + image: hirosystems/stacks-network-orchestrator:latest imagePullPolicy: Always name: chain-coordinator-container ports: diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index 05675dc..f896f5c 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -40,11 +40,13 @@ metadata: namespace: devnet spec: serviceAccountName: stacks-devnet-api-service-account + imagePullSecrets: + - name: stacks-devnet-api-secret containers: - command: - ./stacks-devnet-api name: stacks-devnet-api-container - image: quay.io/hirosystems/stacks-devnet-api:latest + image: hirosystems/stacks-devnet-api:latest imagePullPolicy: Always ports: - containerPort: 8477 From 6dcb36d2845993cd322b0c0dd1f6f9ce6d61f218 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Thu, 31 Aug 2023 14:05:35 -0400 Subject: [PATCH 02/35] feat: check auth header before allowing access (#47) --- Config.toml | 4 ++ src/api_config.rs | 45 +++++++++++++++++++++ src/config.rs | 36 +++++++++++++++-- src/lib.rs | 1 + src/main.rs | 35 +++++++++++++--- src/responder.rs | 36 ++--------------- src/routes.rs | 3 +- src/tests/mod.rs | 101 +++++++++++++++++++++++++++------------------- 8 files changed, 178 insertions(+), 83 deletions(-) create mode 100644 src/api_config.rs diff --git a/Config.toml b/Config.toml index 3d004fd..f3707d7 100644 --- a/Config.toml +++ b/Config.toml @@ -1,2 +1,6 @@ +[http_response] allowed_origins = ["*"] allowed_methods = ["DELETE", "GET", "OPTIONS", "POST", "HEAD"] + +[auth] +auth_header = "x-auth-request-user" \ No newline at end of file diff --git a/src/api_config.rs b/src/api_config.rs new file mode 100644 index 0000000..9ecdb52 --- /dev/null +++ b/src/api_config.rs @@ -0,0 +1,45 @@ +use std::{ + fs::File, + io::{BufReader, Read}, +}; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, serde::Deserialize, Clone, Default)] +pub struct ApiConfig { + #[serde(rename = "http_response")] + pub http_response_config: ResponderConfig, + #[serde(rename = "auth")] + pub auth_config: AuthConfig, +} + +#[derive(Serialize, Deserialize, Clone, Default)] +pub struct ResponderConfig { + pub allowed_origins: Option>, + pub allowed_methods: Option>, +} + +#[derive(Serialize, Deserialize, Clone, Default)] +pub struct AuthConfig { + pub auth_header: Option, +} + +impl ApiConfig { + pub fn from_path(config_path: &str) -> ApiConfig { + let file = File::open(config_path) + .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", config_path, e)); + let mut file_reader = BufReader::new(file); + let mut file_buffer = vec![]; + file_reader + .read_to_end(&mut file_buffer) + .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", config_path, e)); + + let config_file: ApiConfig = match toml::from_slice(&file_buffer) { + Ok(s) => s, + Err(e) => { + panic!("Config file malformatted {}", e.to_string()); + } + }; + config_file + } +} diff --git a/src/config.rs b/src/config.rs index e9a8287..a15a36b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,7 +22,7 @@ pub struct ValidatedStacksDevnetConfig { pub contract_configmap_data: Vec<(String, String)>, pub disable_stacks_api: bool, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct StacksDevnetConfig { pub namespace: String, pub disable_stacks_api: bool, @@ -35,12 +35,24 @@ pub struct StacksDevnetConfig { impl StacksDevnetConfig { pub fn to_validated_config( self, + user_id: &str, ctx: Context, ) -> Result { let context = format!( "failed to validate config for NAMESPACE: {}", self.namespace ); + + if user_id != self.namespace { + let msg = + format!("{context}, ERROR: devnet namespace must match authenticated user id"); + ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); + return Err(DevNetError { + message: msg.into(), + code: 400, + }); + } + let project_manifest_yaml_string = self .get_project_manifest_yaml_string() .map_err(|e| log_and_return_err(e, &context, &ctx))?; @@ -213,11 +225,12 @@ mod tests { #[test] fn it_converts_config_to_yaml() { let template = get_template_config("src/tests/fixtures/stacks-devnet-config.json"); + let user_id = &template.namespace.clone(); let logger = hiro_system_kit::log::setup_logger(); let _guard = hiro_system_kit::log::setup_global_logger(logger.clone()); let ctx = Context::empty(); let validated_config = template - .to_validated_config(ctx) + .to_validated_config(user_id, ctx) .unwrap_or_else(|e| panic!("config validation test failed: {}", e.message)); let expected_project_manifest = read_file("src/tests/fixtures/project-manifest.yaml"); @@ -261,8 +274,25 @@ mod tests { tracer: false, }; template.network_manifest.devnet = None; + let user_id = template.clone().namespace; template - .to_validated_config(ctx) + .to_validated_config(&user_id, ctx) .unwrap_or_else(|e| panic!("config validation test failed: {}", e.message)); } + + #[test] + fn it_rejects_config_with_namespace_user_id_mismatch() { + let template = get_template_config("src/tests/fixtures/stacks-devnet-config.json"); + let namespace = template.namespace.clone(); + let user_id = "wrong"; + match template.to_validated_config(user_id, Context::empty()) { + Ok(_) => { + panic!("config validation with non-matching user_id should have been rejected") + } + Err(e) => { + assert_eq!(e.code, 400); + assert_eq!(e.message, format!("failed to validate config for NAMESPACE: {}, ERROR: devnet namespace must match authenticated user id", namespace)); + } + } + } } diff --git a/src/lib.rs b/src/lib.rs index fb52b77..2384a62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ use config::ValidatedStacksDevnetConfig; mod template_parser; use template_parser::get_yaml_from_resource; +pub mod api_config; pub mod resources; pub mod responder; pub mod routes; diff --git a/src/main.rs b/src/main.rs index 6adb119..05a328f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ use hiro_system_kit::slog; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server}; -use stacks_devnet_api::responder::{Responder, ResponderConfig}; +use stacks_devnet_api::api_config::ApiConfig; +use stacks_devnet_api::responder::Responder; use stacks_devnet_api::routes::{ get_standardized_path_parts, handle_check_devnet, handle_delete_devnet, handle_get_devnet, handle_new_devnet, handle_try_proxy_service, API_PATH, @@ -28,7 +29,7 @@ async fn main() { } else { "/etc/config/Config.toml" }; - let config = ResponderConfig::from_path(config_path); + let config = ApiConfig::from_path(config_path); let make_svc = make_service_fn(|_| { let k8s_manager = k8s_manager.clone(); @@ -53,7 +54,10 @@ async fn main() { async fn handle_request( request: Request, k8s_manager: StacksDevnetApiK8sManager, - config: ResponderConfig, + ApiConfig { + http_response_config, + auth_config, + }: ApiConfig, ctx: Context, ) -> Result, Infallible> { let uri = request.uri(); @@ -67,14 +71,32 @@ async fn handle_request( path ) }); + let headers = request.headers().clone(); + let responder = Responder::new(http_response_config, headers.clone()).unwrap(); + + let auth_header = auth_config + .auth_header + .unwrap_or("x-auth-request-user".to_string()); + let user_id = match headers.get(auth_header) { + Some(auth_header_value) => match auth_header_value.to_str() { + Ok(user_id) => user_id.replace("|", "-"), + Err(e) => { + let msg = format!("unable to parse auth header: {}", &e); + ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); + return responder.err_bad_request(msg); + } + }, + None => return responder.err_bad_request("missing required auth header".into()), + }; - let responder = Responder::new(config, request.headers().clone()).unwrap(); if method == &Method::OPTIONS { return responder.ok(); } if path == "/api/v1/networks" { return match method { - &Method::POST => handle_new_devnet(request, k8s_manager, responder, ctx).await, + &Method::POST => { + handle_new_devnet(request, &user_id, k8s_manager, responder, ctx).await + } _ => responder.err_method_not_allowed("network creation must be a POST request".into()), }; } else if path.starts_with(API_PATH) { @@ -88,6 +110,9 @@ async fn handle_request( return responder.err_bad_request("no network id provided".into()); } let network = path_parts.network.unwrap(); + if network != user_id { + return responder.err_bad_request("network id must match authenticated user id".into()); + } // verify that we have a valid namespace and the network actually exists let exists = match k8s_manager.check_namespace_exists(&network).await { diff --git a/src/responder.rs b/src/responder.rs index d9ce884..b9bf55c 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,9 +1,3 @@ -use std::{ - convert::Infallible, - fs::File, - io::{BufReader, Read}, -}; - use hyper::{ header::{ ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_METHODS, @@ -12,7 +6,9 @@ use hyper::{ http::{response::Builder, HeaderValue}, Body, HeaderMap, Response, StatusCode, }; -use serde::{Deserialize, Serialize}; +use std::convert::Infallible; + +use crate::api_config::ResponderConfig; #[derive(Default)] pub struct Responder { @@ -21,32 +17,6 @@ pub struct Responder { headers: HeaderMap, } -#[derive(Serialize, Deserialize, Clone, Default)] -pub struct ResponderConfig { - pub allowed_origins: Option>, - pub allowed_methods: Option>, -} - -impl ResponderConfig { - pub fn from_path(config_path: &str) -> ResponderConfig { - let file = File::open(config_path) - .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", config_path, e)); - let mut file_reader = BufReader::new(file); - let mut file_buffer = vec![]; - file_reader - .read_to_end(&mut file_buffer) - .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", config_path, e)); - - let config_file: ResponderConfig = match toml::from_slice(&file_buffer) { - Ok(s) => s, - Err(e) => { - panic!("Config file malformatted {}", e.to_string()); - } - }; - config_file - } -} - impl Responder { pub fn new( config: ResponderConfig, diff --git a/src/routes.rs b/src/routes.rs index e7486a4..ecb9c02 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -11,6 +11,7 @@ use crate::{ pub async fn handle_new_devnet( request: Request, + user_id: &str, k8s_manager: StacksDevnetApiK8sManager, responder: Responder, ctx: Context, @@ -24,7 +25,7 @@ pub async fn handle_new_devnet( let body = body.unwrap(); let config: Result = serde_json::from_slice(&body); match config { - Ok(config) => match config.to_validated_config(ctx) { + Ok(config) => match config.to_validated_config(user_id, ctx) { Ok(config) => match k8s_manager.deploy_devnet(config).await { Ok(_) => responder.ok(), Err(e) => responder.respond(e.code, e.message), diff --git a/src/tests/mod.rs b/src/tests/mod.rs index d3af9a7..56f9b6e 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -9,11 +9,12 @@ use super::*; use hyper::{ body, header::{ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN}, - http::HeaderValue, + http::{request::Builder, HeaderValue}, Client, HeaderMap, Method, StatusCode, }; use k8s_openapi::api::core::v1::Namespace; use stacks_devnet_api::{ + api_config::ResponderConfig, config::StacksDevnetConfig, resources::service::{ get_service_from_path_part, get_service_port, get_service_url, ServicePort, @@ -52,6 +53,12 @@ async fn get_k8s_manager() -> (StacksDevnetApiK8sManager, Context) { (k8s_manager, ctx) } +fn get_request_builder(request_path: &str, method: Method, user_id: &str) -> Builder { + Request::builder() + .uri(request_path) + .method(method) + .header("x-auth-request-user", user_id) +} fn get_random_namespace() -> String { let mut rng = rand::thread_rng(); let random_digit: u64 = rand::Rng::gen(&mut rng); @@ -124,13 +131,13 @@ async fn it_responds_to_valid_requests_with_deploy( let (k8s_manager, ctx) = get_k8s_manager().await; - let request_builder = Request::builder().uri(request_path).method(method); + let request_builder = get_request_builder(request_path, method, &namespace); let _ = k8s_manager.deploy_namespace(&namespace).await.unwrap(); let mut config = get_template_config(); config.namespace = namespace.to_owned(); - let validated_config = config.to_validated_config(ctx.clone()).unwrap(); + let validated_config = config.to_validated_config(&namespace, ctx.clone()).unwrap(); let _ = k8s_manager.deploy_devnet(validated_config).await.unwrap(); // short delay to allow assets to start sleep(Duration::new(5, 0)); @@ -145,14 +152,9 @@ async fn it_responds_to_valid_requests_with_deploy( }; let request: Request = request_builder.body(body).unwrap(); - let mut response = handle_request( - request, - k8s_manager.clone(), - ResponderConfig::default(), - ctx, - ) - .await - .unwrap(); + let mut response = handle_request(request, k8s_manager.clone(), ApiConfig::default(), ctx) + .await + .unwrap(); let body = response.body_mut(); let bytes = body::to_bytes(body).await.unwrap().to_vec(); @@ -193,21 +195,16 @@ async fn it_responds_to_valid_requests( let (k8s_manager, ctx) = get_k8s_manager().await; - let request_builder = Request::builder().uri(request_path).method(method); + let request_builder = get_request_builder(request_path, method, &namespace); if set_up { let _ = k8s_manager.deploy_namespace(&namespace).await.unwrap(); } let request: Request = request_builder.body(Body::empty()).unwrap(); - let mut response = handle_request( - request, - k8s_manager.clone(), - ResponderConfig::default(), - ctx, - ) - .await - .unwrap(); + let mut response = handle_request(request, k8s_manager.clone(), ApiConfig::default(), ctx) + .await + .unwrap(); let body = response.body_mut(); let bytes = body::to_bytes(body).await.unwrap().to_vec(); @@ -267,47 +264,68 @@ async fn get_mock_k8s_manager() -> (StacksDevnetApiK8sManager, Context) { (k8s_manager, ctx) } -#[test_case("/path", Method::GET => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /path")] -#[test_case("/api", Method::GET => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api")] -#[test_case("/api/v1", Method::GET => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api/v1")] -#[test_case("/api/v1/network2", Method::GET => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api/v1/network2")] -#[test_case("/api/v1/network/undeployed", Method::GET => +#[test_case("/path", Method::GET, "some-user" => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /path")] +#[test_case("/api", Method::GET, "some-user" => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api")] +#[test_case("/api/v1", Method::GET, "some-user" => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api/v1")] +#[test_case("/api/v1/network2", Method::GET, "some-user" => is equal_to (StatusCode::BAD_REQUEST, "invalid request path".to_string()) ; "400 for invalid requet path /api/v1/network2")] +#[test_case("/api/v1/network/undeployed", Method::GET, "undeployed" => is equal_to (StatusCode::NOT_FOUND, "network undeployed does not exist".to_string()); "404 for undeployed namespace")] -#[test_case("/api/v1/network/500_err", Method::GET => +#[test_case("/api/v1/network/500_err", Method::GET, "500_err" => is equal_to (StatusCode::INTERNAL_SERVER_ERROR, "error getting namespace 500_err: \"\"".to_string()); "forwarded error if fetching namespace returns error")] -#[test_case("/api/v1/network/test", Method::POST => +#[test_case("/api/v1/network/test", Method::POST, "test" => is equal_to (StatusCode::METHOD_NOT_ALLOWED, "can only GET/DELETE/HEAD at provided route".to_string()); "405 for network route with POST request")] -#[test_case("/api/v1/network/test/commands", Method::GET => +#[test_case("/api/v1/network/test/commands", Method::GET, "test" => is equal_to (StatusCode::NOT_FOUND, "commands route in progress".to_string()); "404 for network commands route")] -#[test_case("/api/v1/network/", Method::GET => +#[test_case("/api/v1/network/", Method::GET, "test" => is equal_to (StatusCode::BAD_REQUEST, "no network id provided".to_string()); "400 for missing namespace")] -#[test_case("/api/v1/networks", Method::GET => +#[test_case("/api/v1/networks", Method::GET, "test" => is equal_to (StatusCode::METHOD_NOT_ALLOWED, "network creation must be a POST request".to_string()); "405 for network creation request with GET method")] -#[test_case("/api/v1/networks", Method::DELETE => +#[test_case("/api/v1/networks", Method::DELETE, "test" => is equal_to (StatusCode::METHOD_NOT_ALLOWED, "network creation must be a POST request".to_string()); "405 for network creation request with DELETE method")] -#[test_case("/api/v1/networks", Method::POST => +#[test_case("/api/v1/networks", Method::POST, "test" => is equal_to (StatusCode::BAD_REQUEST, "invalid configuration to create network: EOF while parsing a value at line 1 column 0".to_string()); "400 for network creation request invalid config")] +#[test_case("/api/v1/network/test", Method::GET, "wrong-id" => + is equal_to (StatusCode::BAD_REQUEST, "network id must match authenticated user id".to_string()); "400 for request with non-matching user")] #[tokio::test] async fn it_responds_to_invalid_requests( request_path: &str, method: Method, + user_id: &str, ) -> (StatusCode, String) { let (k8s_manager, ctx) = get_mock_k8s_manager().await; - let request_builder = Request::builder().uri(request_path).method(method); + let request_builder = get_request_builder(request_path, method, &user_id); + let request: Request = request_builder.body(Body::empty()).unwrap(); + let mut response = handle_request(request, k8s_manager.clone(), ApiConfig::default(), ctx) + .await + .unwrap(); + let body = response.body_mut(); + let bytes = body::to_bytes(body).await.unwrap().to_vec(); + let body_str = String::from_utf8(bytes).unwrap(); + (response.status(), body_str) +} + +#[tokio::test] +async fn it_responds_to_invalid_request_header() { + let (k8s_manager, ctx) = get_mock_k8s_manager().await; + + let request_builder = Request::builder() + .uri("/api/v1/network/test") + .method(Method::GET); let request: Request = request_builder.body(Body::empty()).unwrap(); let mut response = handle_request( request, k8s_manager.clone(), - ResponderConfig::default(), - ctx, + ApiConfig::default(), + ctx.clone(), ) .await .unwrap(); let body = response.body_mut(); let bytes = body::to_bytes(body).await.unwrap().to_vec(); let body_str = String::from_utf8(bytes).unwrap(); - (response.status(), body_str) + assert_eq!(response.status(), 400); + assert_eq!(body_str, "missing required auth header".to_string()); } #[test_case("" => is equal_to PathParts { route: String::new(), ..Default::default() }; "for empty path")] @@ -339,7 +357,7 @@ async fn request_mutation_should_create_valid_proxy_destination() { get_service_url(&network, service.clone()), get_service_port(service, ServicePort::RPC).unwrap() ); - let request_builder = Request::builder().uri("/").method("POST"); + let request_builder = get_request_builder("/", Method::POST, "some-network"); let request: Request = request_builder.body(Body::empty()).unwrap(); let request = mutate_request_for_proxy(request, &forward_url, &remainder); let actual_url = request.uri().to_string(); @@ -372,10 +390,11 @@ fn responder_allows_configuring_allowed_origins() { } #[test] -fn responder_config_reads_from_file() { - let config = ResponderConfig::from_path("Config.toml"); - assert!(config.allowed_methods.is_some()); - assert!(config.allowed_origins.is_some()); +fn config_reads_from_file() { + let config = ApiConfig::from_path("Config.toml"); + assert!(config.http_response_config.allowed_methods.is_some()); + assert!(config.http_response_config.allowed_origins.is_some()); + assert!(config.auth_config.auth_header.is_some()); } #[tokio::test] From ba6ce13d325c9bfca25e4f2674e7b73140f17865 Mon Sep 17 00:00:00 2001 From: deantchi <21262275+deantchi@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:53:46 -0700 Subject: [PATCH 03/35] Devops-1286 - CI workflow (#49) Co-authored-by: MicaiahReid --- .github/workflows/ci.yaml | 184 +++++++++++++++++++ .releaserc | 22 +++ Dockerfile | 14 +- src/tests/mod.rs | 9 +- templates/ci/stacks-devnet-api.template.yaml | 76 ++++++++ templates/stacks-devnet-api.template.yaml | 3 +- 6 files changed, 294 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 .releaserc create mode 100644 templates/ci/stacks-devnet-api.template.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..8acbe5c --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,184 @@ +name: CI + +on: + push: + branches: + - main + - develop + tags-ignore: + - "**" + paths-ignore: + - "**/CHANGELOG.md" + - "**/package.json" + pull_request: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + outputs: + version_output: ${{ steps.meta.outputs.version }} + steps: + - uses: actions/checkout@v2 + with: + token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + fetch-depth: 0 + persist-credentials: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Docker Meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + hirosystems/${{ github.event.repository.name }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} + type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Create artifact directory + run: mkdir -p /tmp/artifacts + + - name: Build/Save Image + uses: docker/build-push-action@v2 + with: + context: . + tags: ${{ steps.meta.outputs.tags }}, + labels: ${{ steps.meta.outputs.labels }} + outputs: type=docker,dest=/tmp/artifacts/myimage.tar + + - name: Save docker artifact + uses: actions/upload-artifact@v3 + with: + name: docker-image + path: /tmp/artifacts/myimage.tar + + k8s-tests: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Read version into env var + env: + version: ${{ needs.build.outputs.version_output }} + run: | + echo "version_tag=$version" >> "$GITHUB_ENV" + echo "Extracted version tag: $version_tag" + + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Build k8s cluster + uses: nolar/setup-k3d-k3s@v1 + with: + version: v1.26 + k3d-name: k3d-kube + k3d-args: "--no-lb --no-rollback --k3s-arg --disable=traefik,servicelb,metrics-server@server:*" + + - name: Pull docker image artifact from previous docker job + uses: actions/download-artifact@v3 + with: + name: docker-image + path: /tmp/artifacts + + - name: Load image + run: | + docker load --input /tmp/artifacts/myimage.tar + docker tag hirosystems/stacks-devnet-api:$version_tag hirosystems/stacks-devnet-api:ci + docker image ls -a + + - name: Deploy k8s manifests + run: | + k3d image import hirosystems/stacks-devnet-api:ci -c k3d-kube + kubectl create namespace devnet + kubectl create configmap stacks-devnet-api-conf --from-file=./Config.toml --namespace devnet + kubectl apply -f ./templates/ci/stacks-devnet-api.template.yaml + echo "sleep for 30 sec" + sleep 30 + + - name: Sanity check on k8s resources deployed + run: | + kubectl get all --all-namespaces + kubectl -n devnet get cm + kubectl -n devnet describe po stacks-devnet-api + kubectl -n devnet logs stacks-devnet-api + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Install and run cargo-tarpaulin + run: | + cargo install cargo-tarpaulin + cargo --version + cargo tarpaulin --out Lcov + + - name: Upload to codecov.io + uses: codecov/codecov-action@v2 + with: + token: ${{secrets.CODECOV_TOKEN}} + + build-publish-release: + runs-on: ubuntu-latest + needs: + - build + - k8s-tests + if: needs.build.result == 'success' && needs.k8s-tests.result == 'success' + steps: + - uses: actions/checkout@v2 + with: + token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + fetch-depth: 0 + persist-credentials: false + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v3 + id: semantic + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + SEMANTIC_RELEASE_PACKAGE: ${{ github.event.repository.name }} + with: + semantic_version: 19 + extra_plugins: | + @semantic-release/changelog@6.0.3 + @semantic-release/git@10.0.1 + conventional-changelog-conventionalcommits@6.1.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Docker Meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + hirosystems/${{ github.event.repository.name }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} + type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build/Push Image + uses: docker/build-push-action@v4 + with: + context: . + tags: ${{ steps.meta.outputs.tags }}, + labels: ${{ steps.meta.outputs.labels }} + # Only push if (there's a new release on main branch, or if building a non-main branch) and (Only run on non-PR events or only PRs that aren't from forks) + push: ${{ (github.ref != 'refs/heads/main' || steps.semantic.outputs.new_release_version != '') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }} diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..76466b6 --- /dev/null +++ b/.releaserc @@ -0,0 +1,22 @@ +{ + "branches": [ + "main" + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits" + } + ], + "@semantic-release/github", + "@semantic-release/changelog", + "@semantic-release/git" + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e98f24c..d2278f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,13 @@ -FROM arm64v8/rust:1.67 as builder - -WORKDIR ./ -COPY . ./ +FROM rust:bullseye as builder +RUN apt update && apt install -y ca-certificates pkg-config libssl-dev +WORKDIR /src +COPY . /src +RUN mkdir /out RUN cargo build --release --manifest-path ./Cargo.toml +RUN cp target/release/stacks-devnet-api /out FROM gcr.io/distroless/cc -COPY --from=builder target/release/stacks-devnet-api / +COPY --from=builder /out/ /bin/ -ENTRYPOINT ["./stacks-devnet-api"] \ No newline at end of file +CMD ["stacks-devnet-api"] \ No newline at end of file diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 56f9b6e..d67c0ec 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -404,11 +404,8 @@ async fn main_starts_server() { }); sleep(Duration::new(1, 0)); let client = Client::new(); - let request_builder = Request::builder() - .uri("http://localhost:8477") - .method(Method::OPTIONS) - .body(Body::empty()) - .unwrap(); - let response = client.request(request_builder).await; + let request_builder = get_request_builder("http://localhost:8477", Method::OPTIONS, "user-id"); + let request = request_builder.body(Body::empty()).unwrap(); + let response = client.request(request).await; assert_eq!(response.unwrap().status(), 200); } diff --git a/templates/ci/stacks-devnet-api.template.yaml b/templates/ci/stacks-devnet-api.template.yaml new file mode 100644 index 0000000..711243f --- /dev/null +++ b/templates/ci/stacks-devnet-api.template.yaml @@ -0,0 +1,76 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: stacks-devnet-api-service-account + namespace: devnet + +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: stacks-devnet-api-service-account +rules: + - apiGroups: [""] + # TODO: production version should not be able to create/delete namespaces (only get) + resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims", "namespaces"] + verbs: ["get", "delete", "create"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: stacks-devnet-api-service-account +subjects: + - kind: ServiceAccount + name: stacks-devnet-api-service-account + namespace: devnet +roleRef: + kind: ClusterRole + name: stacks-devnet-api-service-account + apiGroup: rbac.authorization.k8s.io + +--- + +apiVersion: v1 +kind: Pod +metadata: + labels: + name: stacks-devnet-api + name: stacks-devnet-api + namespace: devnet +spec: + serviceAccountName: stacks-devnet-api-service-account + containers: + - command: ["stacks-devnet-api"] + name: stacks-devnet-api-container + image: hirosystems/stacks-devnet-api:ci + imagePullPolicy: Never + ports: + - containerPort: 8477 + name: api + protocol: TCP + volumeMounts: + - name: config-volume + mountPath: /etc/config + volumes: + - name: config-volume + configMap: + name: stacks-devnet-api-conf + +--- +apiVersion: v1 +kind: Service +metadata: + name: stacks-devnet-api-service + namespace: devnet +spec: + ports: + - name: api + port: 8477 + protocol: TCP + targetPort: 8477 + nodePort: 30000 + selector: + name: stacks-devnet-api + type: NodePort + diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index f896f5c..5aaf233 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -43,8 +43,7 @@ spec: imagePullSecrets: - name: stacks-devnet-api-secret containers: - - command: - - ./stacks-devnet-api + - command: ["stacks-devnet-api"] name: stacks-devnet-api-container image: hirosystems/stacks-devnet-api:latest imagePullPolicy: Always From 8aac51eef601d0504e82d4b993d5f3427e4b43d5 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Fri, 1 Sep 2023 14:54:32 -0400 Subject: [PATCH 04/35] ci: build for multiple platforms (#50) --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8acbe5c..8f6520a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -180,5 +180,6 @@ jobs: context: . tags: ${{ steps.meta.outputs.tags }}, labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 # Only push if (there's a new release on main branch, or if building a non-main branch) and (Only run on non-PR events or only PRs that aren't from forks) push: ${{ (github.ref != 'refs/heads/main' || steps.semantic.outputs.new_release_version != '') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }} From 9fe79725c6f8935222c9f291d6f2e591731d64c8 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Fri, 1 Sep 2023 19:26:51 -0400 Subject: [PATCH 05/35] fix: don't require auth on OPTIONS requests; configure user-id prefix to make namespace (#51) --- Config.toml | 3 ++- src/api_config.rs | 4 ++++ src/main.rs | 17 ++++++++++---- src/tests/mod.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/Config.toml b/Config.toml index f3707d7..c8cf001 100644 --- a/Config.toml +++ b/Config.toml @@ -3,4 +3,5 @@ allowed_origins = ["*"] allowed_methods = ["DELETE", "GET", "OPTIONS", "POST", "HEAD"] [auth] -auth_header = "x-auth-request-user" \ No newline at end of file +auth_header = "x-auth-request-user" +namespace_prefix = "platform-" \ No newline at end of file diff --git a/src/api_config.rs b/src/api_config.rs index 9ecdb52..49a191e 100644 --- a/src/api_config.rs +++ b/src/api_config.rs @@ -22,6 +22,10 @@ pub struct ResponderConfig { #[derive(Serialize, Deserialize, Clone, Default)] pub struct AuthConfig { pub auth_header: Option, + /// When the auth header is retrieved from the request, + /// this value will be prepended to the string to create + /// the k8s namespace. + pub namespace_prefix: Option, } impl ApiConfig { diff --git a/src/main.rs b/src/main.rs index 05a328f..bf3a6f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,12 +74,24 @@ async fn handle_request( let headers = request.headers().clone(); let responder = Responder::new(http_response_config, headers.clone()).unwrap(); + if method == &Method::OPTIONS { + return responder.ok(); + } let auth_header = auth_config .auth_header .unwrap_or("x-auth-request-user".to_string()); let user_id = match headers.get(auth_header) { Some(auth_header_value) => match auth_header_value.to_str() { - Ok(user_id) => user_id.replace("|", "-"), + Ok(user_id) => { + let user_id = user_id.replace("|", "-"); + match auth_config.namespace_prefix { + Some(mut prefix) => { + prefix.push_str(&user_id); + prefix + } + None => user_id, + } + } Err(e) => { let msg = format!("unable to parse auth header: {}", &e); ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); @@ -89,9 +101,6 @@ async fn handle_request( None => return responder.err_bad_request("missing required auth header".into()), }; - if method == &Method::OPTIONS { - return responder.ok(); - } if path == "/api/v1/networks" { return match method { &Method::POST => { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index d67c0ec..2f908b0 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -14,7 +14,7 @@ use hyper::{ }; use k8s_openapi::api::core::v1::Namespace; use stacks_devnet_api::{ - api_config::ResponderConfig, + api_config::{AuthConfig, ResponderConfig}, config::StacksDevnetConfig, resources::service::{ get_service_from_path_part, get_service_port, get_service_url, ServicePort, @@ -328,6 +328,25 @@ async fn it_responds_to_invalid_request_header() { assert_eq!(body_str, "missing required auth header".to_string()); } +#[tokio::test] +async fn it_ignores_request_header_for_options_requests() { + let (k8s_manager, ctx) = get_mock_k8s_manager().await; + + let request_builder = Request::builder() + .uri("/api/v1/network/test") + .method(Method::OPTIONS); + let request: Request = request_builder.body(Body::empty()).unwrap(); + let response = handle_request( + request, + k8s_manager.clone(), + ApiConfig::default(), + ctx.clone(), + ) + .await + .unwrap(); + assert_eq!(response.status(), 200); +} + #[test_case("" => is equal_to PathParts { route: String::new(), ..Default::default() }; "for empty path")] #[test_case("/api/v1/" => is equal_to PathParts { route: String::new(), ..Default::default() }; "for /api/v1/ path")] #[test_case("/api/v1/some-route" => is equal_to PathParts { route: String::from("some-route"), ..Default::default() }; "for /api/v1/some-route path")] @@ -389,12 +408,49 @@ fn responder_allows_configuring_allowed_origins() { ); } +#[serial_test::serial] +#[tokio::test] +async fn namespace_prefix_config_prepends_header() { + let (k8s_manager, ctx) = get_k8s_manager().await; + + // using the ApiConfig's `namespace_prefix` field will add the prefix + // before the `user_id` as the authenticated user, which should match the request path + let namespace = &get_random_namespace(); + let _ = k8s_manager.deploy_namespace(&namespace).await.unwrap(); + + let (namespace_prefix, user_id) = namespace.split_at(4); + let api_config = ApiConfig { + auth_config: AuthConfig { + namespace_prefix: Some(namespace_prefix.to_string()), + ..Default::default() + }, + ..Default::default() + }; + + let request_builder = get_request_builder( + &format!("/api/v1/network/{namespace}"), + Method::HEAD, + user_id, + ); + let request: Request = request_builder.body(Body::empty()).unwrap(); + let mut response = handle_request(request, k8s_manager.clone(), api_config, ctx.clone()) + .await + .unwrap(); + + let body = response.body_mut(); + let bytes = body::to_bytes(body).await.unwrap().to_vec(); + let body_str = String::from_utf8(bytes).unwrap(); + assert_eq!(response.status(), 404); + assert_eq!(body_str, "not found"); +} + #[test] fn config_reads_from_file() { let config = ApiConfig::from_path("Config.toml"); assert!(config.http_response_config.allowed_methods.is_some()); assert!(config.http_response_config.allowed_origins.is_some()); assert!(config.auth_config.auth_header.is_some()); + assert!(config.auth_config.namespace_prefix.is_some()); } #[tokio::test] From 225d2c9d4d200528a64401418922c163a7c45350 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Tue, 12 Sep 2023 19:23:09 -0400 Subject: [PATCH 06/35] fix: add ACCESS_CONTROL_ALLOW_HEADERS to response header (#52) --- src/api_config.rs | 1 + src/responder.rs | 10 +++++++--- src/tests/mod.rs | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/api_config.rs b/src/api_config.rs index 49a191e..f7f4bbf 100644 --- a/src/api_config.rs +++ b/src/api_config.rs @@ -17,6 +17,7 @@ pub struct ApiConfig { pub struct ResponderConfig { pub allowed_origins: Option>, pub allowed_methods: Option>, + pub allowed_headers: Option, } #[derive(Serialize, Deserialize, Clone, Default)] diff --git a/src/responder.rs b/src/responder.rs index b9bf55c..b359c30 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,7 +1,7 @@ use hyper::{ header::{ - ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_ORIGIN, ORIGIN, + ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, + ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ORIGIN, }, http::{response::Builder, HeaderValue}, Body, HeaderMap, Response, StatusCode, @@ -14,6 +14,7 @@ use crate::api_config::ResponderConfig; pub struct Responder { allowed_origins: Vec, allowed_methods: Vec, + allowed_headers: String, headers: HeaderMap, } @@ -25,12 +26,15 @@ impl Responder { Ok(Responder { allowed_origins: config.allowed_origins.unwrap_or_default(), allowed_methods: config.allowed_methods.unwrap_or_default(), + allowed_headers: config.allowed_headers.unwrap_or("*".to_string()), headers, }) } pub fn response_builder(&self) -> Builder { - let mut builder = Response::builder().header(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); + let mut builder = Response::builder() + .header(ACCESS_CONTROL_ALLOW_CREDENTIALS, "true") + .header(ACCESS_CONTROL_ALLOW_HEADERS, &self.allowed_headers); for method in &self.allowed_methods { builder = builder.header(ACCESS_CONTROL_ALLOW_METHODS, method); diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 2f908b0..ffc8b2d 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -395,6 +395,7 @@ fn responder_allows_configuring_allowed_origins() { let config = ResponderConfig { allowed_origins: Some(vec!["*".to_string()]), allowed_methods: Some(vec!["GET".to_string()]), + allowed_headers: None, }; let mut headers = HeaderMap::new(); headers.append("ORIGIN", HeaderValue::from_str("example.com").unwrap()); From 379529809c16e62f95836ab14c3a6697885614cf Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Wed, 13 Sep 2023 12:28:36 -0400 Subject: [PATCH 07/35] chore: update clarinet dependency (#53) --- Cargo.lock | 29 +- Cargo.toml | 6 +- examples/new-network.example.json | 312 +++++++++---------- src/tests/fixtures/project-manifest.yaml | 2 +- src/tests/fixtures/stacks-devnet-config.json | 2 +- 5 files changed, 161 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 207604a..d78dc7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,9 +193,9 @@ checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64-compat" @@ -399,11 +399,12 @@ dependencies = [ [[package]] name = "clarinet-deployments" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad2b850465d600a43e35e24a4a250504f946757bfcdd57d38ce012ffee2aa29" +checksum = "e5b484bdc8880ac7cc3fff38d1cd17d957685af68f8dde2bba82878c48381bb4" dependencies = [ "base58 0.2.0", + "base64 0.21.4", "bitcoin", "bitcoincore-rpc", "bitcoincore-rpc-json", @@ -422,9 +423,9 @@ dependencies = [ [[package]] name = "clarinet-files" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc42d784155c3dc4f578f14f41fd7fa4f01c3354a8b5a93e5cbe966cf002cce" +checksum = "7dc81af3686bcf35e8f8bfe844473f1af3372a7e148561378f6598a871dc018b" dependencies = [ "bip39", "bitcoin", @@ -454,9 +455,9 @@ dependencies = [ [[package]] name = "clarity-repl" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b21c5cf2058527e435fbfb0040dd715190989a020c9ae7bcebce4cf4b3aabb9" +checksum = "7ce75a1ed41a078a67f78abe73c84c241331044304831af8db8b7056dcef46fc" dependencies = [ "ansi_term", "atty", @@ -1420,7 +1421,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd990069640f9db34b3b0f7a1afc62a05ffaa3be9b66aa3c313f58346df7f788" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "chrono", "http", @@ -2167,7 +2168,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -2328,7 +2329,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", ] [[package]] @@ -2868,9 +2869,9 @@ dependencies = [ [[package]] name = "stacks-rpc-client" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ed9348051a94c97b7173792fd8d1308a2e89dada5da920dd22ac629464e6cb" +checksum = "12eef9ff174e8345b414abbfa5ed7e271e772d1e6b2c3eaa9f7f02209c87f48e" dependencies = [ "clarity-repl", "hmac 0.12.1", @@ -3346,7 +3347,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ac8060a61f8758a61562f6fb53ba3cbe1ca906f001df2e53cccddcdbee91e7c" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "bitflags 2.3.3", "bytes", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index befee83..ec5b592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,10 +26,10 @@ hiro-system-kit = {version = "0.1.0", features = ["log"]} strum_macros = "0.24.3" strum = "0.24.1" clarity-vm = "2.1.1" -clarity-repl = "1.7.2" -clarinet-files = {version = "1.0.2" } +clarity-repl = "1.8.0" +clarinet-files = {version = "1.0.3" } chainhook-types = "1.0" -clarinet-deployments = {version = "1.0.2" } +clarinet-deployments = {version = "1.0.3" } toml = "0.5.9" [dev-dependencies] diff --git a/examples/new-network.example.json b/examples/new-network.example.json index 1fcf0f5..92c5931 100644 --- a/examples/new-network.example.json +++ b/examples/new-network.example.json @@ -1,6 +1,4 @@ { - "namespace": "test-namespace", - "disable_stacks_api": false, "deployment_plan": { "id": 0, "name": "Devnet deployment", @@ -8,150 +6,125 @@ "stacks_node": "http://localhost:20443", "bitcoin_node": "http://px-devnet:px-devnet@localhost:18443", "genesis": null, - "plan": { - "batches": [ - { - "id": 0, - "transactions": [ - { - "RequirementPublish": { - "contract_id": { - "name": "nft-trait", - "issuer": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT" - }, - "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "remap_principals": { - "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - "source": "KGRlZmluZS10cmFpdCBuZnQtdHJhaXQKICAoCiAgICA7OyBMYXN0IHRva2VuIElELCBsaW1pdGVkIHRvIHVpbnQgcmFuZ2UKICAgIChnZXQtbGFzdC10b2tlbi1pZCAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyBVUkkgZm9yIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgdG9rZW4KICAgIChnZXQtdG9rZW4tdXJpICh1aW50KSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctYXNjaWkgMjU2KSkgdWludCkpCgogICAgIDs7IE93bmVyIG9mIGEgZ2l2ZW4gdG9rZW4gaWRlbnRpZmllcgogICAgKGdldC1vd25lciAodWludCkgKHJlc3BvbnNlIChvcHRpb25hbCBwcmluY2lwYWwpIHVpbnQpKQoKICAgIDs7IFRyYW5zZmVyIGZyb20gdGhlIHNlbmRlciB0byBhIG5ldyBwcmluY2lwYWwKICAgICh0cmFuc2ZlciAodWludCBwcmluY2lwYWwgcHJpbmNpcGFsKSAocmVzcG9uc2UgYm9vbCB1aW50KSkKICApCik=", - "clarity_version": "Clarity1", - "cost": 4670, - "location": { - "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.clar" - } - } - } - ], - "epoch": "2.0" - }, - { - "id": 1, - "transactions": [ - { - "RequirementPublish": { - "contract_id": { - "issuer": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT", - "name": "sip-010-trait-ft-standard" - }, - "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "remap_principals": { - "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - "source": "KGRlZmluZS10cmFpdCBzaXAtMDEwLXRyYWl0CiAgKAogICAgOzsgVHJhbnNmZXIgZnJvbSB0aGUgY2FsbGVyIHRvIGEgbmV3IHByaW5jaXBhbAogICAgKHRyYW5zZmVyICh1aW50IHByaW5jaXBhbCBwcmluY2lwYWwgKG9wdGlvbmFsIChidWZmIDM0KSkpIChyZXNwb25zZSBib29sIHVpbnQpKQoKICAgIDs7IHRoZSBodW1hbiByZWFkYWJsZSBuYW1lIG9mIHRoZSB0b2tlbgogICAgKGdldC1uYW1lICgpIChyZXNwb25zZSAoc3RyaW5nLWFzY2lpIDMyKSB1aW50KSkKCiAgICA7OyB0aGUgdGlja2VyIHN5bWJvbCwgb3IgZW1wdHkgaWYgbm9uZQogICAgKGdldC1zeW1ib2wgKCkgKHJlc3BvbnNlIChzdHJpbmctYXNjaWkgMzIpIHVpbnQpKQoKICAgIDs7IHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdXNlZCwgZS5nLiA2IHdvdWxkIG1lYW4gMV8wMDBfMDAwIHJlcHJlc2VudHMgMSB0b2tlbgogICAgKGdldC1kZWNpbWFscyAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyB0aGUgYmFsYW5jZSBvZiB0aGUgcGFzc2VkIHByaW5jaXBhbAogICAgKGdldC1iYWxhbmNlIChwcmluY2lwYWwpIChyZXNwb25zZSB1aW50IHVpbnQpKQoKICAgIDs7IHRoZSBjdXJyZW50IHRvdGFsIHN1cHBseSAod2hpY2ggZG9lcyBub3QgbmVlZCB0byBiZSBhIGNvbnN0YW50KQogICAgKGdldC10b3RhbC1zdXBwbHkgKCkgKHJlc3BvbnNlIHVpbnQgdWludCkpCgogICAgOzsgYW4gb3B0aW9uYWwgVVJJIHRoYXQgcmVwcmVzZW50cyBtZXRhZGF0YSBvZiB0aGlzIHRva2VuCiAgICAoZ2V0LXRva2VuLXVyaSAoKSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctdXRmOCAyNTYpKSB1aW50KSkKICApCik=", - "clarity_version": "Clarity1", - "cost": 8390, - "location": { - "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.clar" - } - } + "batches": [ + { + "id": 0, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "source": "KGRlZmluZS10cmFpdCBuZnQtdHJhaXQKICAoCiAgICA7OyBMYXN0IHRva2VuIElELCBsaW1pdGVkIHRvIHVpbnQgcmFuZ2UKICAgIChnZXQtbGFzdC10b2tlbi1pZCAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyBVUkkgZm9yIG1ldGFkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGUgdG9rZW4KICAgIChnZXQtdG9rZW4tdXJpICh1aW50KSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctYXNjaWkgMjU2KSkgdWludCkpCgogICAgIDs7IE93bmVyIG9mIGEgZ2l2ZW4gdG9rZW4gaWRlbnRpZmllcgogICAgKGdldC1vd25lciAodWludCkgKHJlc3BvbnNlIChvcHRpb25hbCBwcmluY2lwYWwpIHVpbnQpKQoKICAgIDs7IFRyYW5zZmVyIGZyb20gdGhlIHNlbmRlciB0byBhIG5ldyBwcmluY2lwYWwKICAgICh0cmFuc2ZlciAodWludCBwcmluY2lwYWwgcHJpbmNpcGFsKSAocmVzcG9uc2UgYm9vbCB1aW50KSkKICApCik=", + "clarity_version": 1, + "cost": 4670, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.nft-trait.clar" } - ], - "epoch": "2.05" - }, - { - "id": 2, - "transactions": [ - { - "RequirementPublish": { - "contract_id": { - "issuer": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9", - "name": "subnet-traits-v1" - }, - "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "remap_principals": { - "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - "source": "OzsgSW4gb3JkZXIgdG8gc3VwcG9ydCB3aXRoZHJhd2luZyBhbiBhc3NldCB0aGF0IHdhcyBtaW50ZWQgb24gYSBzdWJuZXQsIHRoZQo7OyBMMSBjb250cmFjdCBtdXN0IGltcGxlbWVudCB0aGlzIHRyYWl0LgooZGVmaW5lLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQKICAoCiAgICA7OyBQcm9jZXNzIGEgd2l0aGRyYXdhbCBmcm9tIHRoZSBzdWJuZXQgZm9yIGFuIGFzc2V0IHdoaWNoIGRvZXMgbm90IHlldAogICAgOzsgZXhpc3Qgb24gdGhpcyBuZXR3b3JrLCBhbmQgdGh1cyByZXF1aXJlcyBhIG1pbnQuCiAgICAobWludC1mcm9tLXN1Ym5ldAogICAgICAoCiAgICAgICAgdWludCAgICAgICA7OyBhc3NldC1pZCAoTkZUKSBvciBhbW91bnQgKEZUKQogICAgICAgIHByaW5jaXBhbCAgOzsgc2VuZGVyCiAgICAgICAgcHJpbmNpcGFsICA7OyByZWNpcGllbnQKICAgICAgKQogICAgICAocmVzcG9uc2UgYm9vbCB1aW50KQogICAgKQogICkKKQ==", - "clarity_version": "Clarity2", - "cost": 4810, - "location": { - "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1.clar" - } - } + } + ], + "epoch": "2.0" + }, + { + "id": 1, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" }, - { - "RequirementPublish": { - "contract_id": { - "issuer": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9", - "name": "subnet-v1-2" - }, - "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "remap_principals": { - "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - }, - "source": "OzsgVGhlIC5zdWJuZXQgY29udHJhY3QKCihkZWZpbmUtY29uc3RhbnQgQ09OVFJBQ1RfQUREUkVTUyAoYXMtY29udHJhY3QgdHgtc2VuZGVyKSkKCjs7IEVycm9yIGNvZGVzCihkZWZpbmUtY29uc3RhbnQgRVJSX0JMT0NLX0FMUkVBRFlfQ09NTUlUVEVEIDEpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUlORVIgMikKKGRlZmluZS1jb25zdGFudCBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQgMykKKGRlZmluZS1jb25zdGFudCBFUlJfVFJBTlNGRVJfRkFJTEVEIDQpCihkZWZpbmUtY29uc3RhbnQgRVJSX0RJU0FMTE9XRURfQVNTRVQgNSkKKGRlZmluZS1jb25zdGFudCBFUlJfQVNTRVRfQUxSRUFEWV9BTExPV0VEIDYpCihkZWZpbmUtY29uc3RhbnQgRVJSX01FUktMRV9ST09UX0RPRVNfTk9UX01BVENIIDcpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QgOCkKKGRlZmluZS1jb25zdGFudCBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCA5KQooZGVmaW5lLWNvbnN0YW50IEVSUl9WQUxJREFUSU9OX0ZBSUxFRCAxMCkKOzs7IFRoZSB2YWx1ZSBzdXBwbGllZCBmb3IgYHRhcmdldC1jaGFpbi10aXBgIGRvZXMgbm90IG1hdGNoIHRoZSBjdXJyZW50IGNoYWluIHRpcC4KKGRlZmluZS1jb25zdGFudCBFUlJfSU5WQUxJRF9DSEFJTl9USVAgMTEpCjs7OyBUaGUgY29udHJhY3Qgd2FzIGNhbGxlZCBiZWZvcmUgcmVhY2hpbmcgdGhpcy1jaGFpbiBoZWlnaHQgcmVhY2hlcyAxLgooZGVmaW5lLWNvbnN0YW50IEVSUl9DQUxMRURfVE9PX0VBUkxZIDEyKQooZGVmaW5lLWNvbnN0YW50IEVSUl9NSU5UX0ZBSUxFRCAxMykKKGRlZmluZS1jb25zdGFudCBFUlJfQVRURU1QVF9UT19UUkFOU0ZFUl9aRVJPX0FNT1VOVCAxNCkKKGRlZmluZS1jb25zdGFudCBFUlJfSU5fQ09NUFVUQVRJT04gMTUpCjs7IFRoZSBjb250cmFjdCBkb2VzIG5vdCBvd24gdGhpcyBORlQgdG8gd2l0aGRyYXcgaXQuCihkZWZpbmUtY29uc3RhbnQgRVJSX05GVF9OT1RfT1dORURfQllfQ09OVFJBQ1QgMTYpCihkZWZpbmUtY29uc3RhbnQgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQgMzApCgo7OyBNYXAgZnJvbSBTdGFja3MgYmxvY2sgaGVpZ2h0IHRvIGJsb2NrIGNvbW1pdAooZGVmaW5lLW1hcCBibG9jay1jb21taXRzIHVpbnQgKGJ1ZmYgMzIpKQo7OyBNYXAgcmVjb3JkaW5nIHdpdGhkcmF3YWwgcm9vdHMKKGRlZmluZS1tYXAgd2l0aGRyYXdhbC1yb290cy1tYXAgKGJ1ZmYgMzIpIGJvb2wpCjs7IE1hcCByZWNvcmRpbmcgcHJvY2Vzc2VkIHdpdGhkcmF3YWwgbGVhdmVzCihkZWZpbmUtbWFwIHByb2Nlc3NlZC13aXRoZHJhd2FsLWxlYXZlcy1tYXAgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogKGJ1ZmYgMzIpLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogKGJ1ZmYgMzIpIH0gYm9vbCkKCjs7IHByaW5jaXBhbCB0aGF0IGNhbiBjb21taXQgYmxvY2tzCihkZWZpbmUtZGF0YS12YXIgbWluZXIgcHJpbmNpcGFsIHR4LXNlbmRlcikKOzsgcHJpbmNpcGFsIHRoYXQgY2FuIHJlZ2lzdGVyIGNvbnRyYWN0cwooZGVmaW5lLWRhdGEtdmFyIGFkbWluIHByaW5jaXBhbCB0eC1zZW5kZXIpCgo7OyBNYXAgb2YgYWxsb3dlZCBjb250cmFjdHMgZm9yIGFzc2V0IHRyYW5zZmVycyAtIG1hcHMgTDEgY29udHJhY3QgcHJpbmNpcGFsIHRvIEwyIGNvbnRyYWN0IHByaW5jaXBhbAooZGVmaW5lLW1hcCBhbGxvd2VkLWNvbnRyYWN0cyBwcmluY2lwYWwgcHJpbmNpcGFsKQoKOzsgVXNlIHRyYWl0IGRlY2xhcmF0aW9ucwoodXNlLXRyYWl0IG5mdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQubmZ0LXRyYWl0Lm5mdC10cmFpdCkKKHVzZS10cmFpdCBmdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQuc2lwLTAxMC10cmFpdC1mdC1zdGFuZGFyZC5zaXAtMDEwLXRyYWl0KQoodXNlLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQgLnN1Ym5ldC10cmFpdHMtdjEubWludC1mcm9tLXN1Ym5ldC10cmFpdCkKCjs7IFVwZGF0ZSB0aGUgbWluZXIgZm9yIHRoaXMgY29udHJhY3QuCihkZWZpbmUtcHVibGljICh1cGRhdGUtbWluZXIgKG5ldy1taW5lciBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgKGFzc2VydHMhIChpcy1lcSB0eC1zZW5kZXIgKHZhci1nZXQgbWluZXIpKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKICAgICAgICAob2sgKHZhci1zZXQgbWluZXIgbmV3LW1pbmVyKSkKICAgICkKKQoKOzsgUmVnaXN0ZXIgYSBuZXcgRlQgY29udHJhY3QgdG8gYmUgc3VwcG9ydGVkIGJ5IHRoaXMgc3VibmV0LgooZGVmaW5lLXB1YmxpYyAocmVnaXN0ZXItbmV3LWZ0LWNvbnRyYWN0IChmdC1jb250cmFjdCA8ZnQtdHJhaXQ+KSAobDItY29udHJhY3QgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFZlcmlmeSB0aGF0IHR4LXNlbmRlciBpcyBhbiBhdXRob3JpemVkIGFkbWluCiAgICAgICAgKGFzc2VydHMhIChpcy1hZG1pbiB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBTZXQgdXAgdGhlIGFzc2V0cyB0aGF0IHRoZSBjb250cmFjdCBpcyBhbGxvd2VkIHRvIHRyYW5zZmVyCiAgICAgICAgKGFzc2VydHMhIChtYXAtaW5zZXJ0IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAiZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdDogKGNvbnRyYWN0LW9mIGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbDItY29udHJhY3Q6IGwyLWNvbnRyYWN0CiAgICAgICAgfSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IFJlZ2lzdGVyIGEgbmV3IE5GVCBjb250cmFjdCB0byBiZSBzdXBwb3J0ZWQgYnkgdGhpcyBzdWJuZXQuCihkZWZpbmUtcHVibGljIChyZWdpc3Rlci1uZXctbmZ0LWNvbnRyYWN0IChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pIChsMi1jb250cmFjdCBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgOzsgVmVyaWZ5IHRoYXQgdHgtc2VuZGVyIGlzIGFuIGF1dGhvcml6ZWQgYWRtaW4KICAgICAgICAoYXNzZXJ0cyEgKGlzLWFkbWluIHR4LXNlbmRlcikgKGVyciBFUlJfSU5WQUxJRF9NSU5FUikpCgogICAgICAgIDs7IFNldCB1cCB0aGUgYXNzZXRzIHRoYXQgdGhlIGNvbnRyYWN0IGlzIGFsbG93ZWQgdG8gdHJhbnNmZXIKICAgICAgICAoYXNzZXJ0cyEgKG1hcC1pbnNlcnQgYWxsb3dlZC1jb250cmFjdHMgKGNvbnRyYWN0LW9mIG5mdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAibmZ0IiwKICAgICAgICAgICAgbDEtY29udHJhY3Q6IChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpLAogICAgICAgICAgICBsMi1jb250cmFjdDogbDItY29udHJhY3QKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhIG1pbmVyCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGlzLW1pbmVyIChtaW5lci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIG1pbmVyLXRvLWNoZWNrICh2YXItZ2V0IG1pbmVyKSkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhbiBhZG1pbgo7OyBSZXR1cm5zIGJvb2wKKGRlZmluZS1wcml2YXRlIChpcy1hZG1pbiAoYWRkci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIGFkZHItdG8tY2hlY2sgKHZhci1nZXQgYWRtaW4pKQopCgo7OyBIZWxwZXIgZnVuY3Rpb246IGRldGVybWluZXMgd2hldGhlciB0aGUgY29tbWl0LWJsb2NrIG9wZXJhdGlvbiBzYXRpc2ZpZXMgcHJlLWNvbmRpdGlvbnMKOzsgbGlzdGVkIGluIGBjb21taXQtYmxvY2tgLgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChjYW4tY29tbWl0LWJsb2NrPyAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KSAgKHRhcmdldC1jaGFpbi10aXAgKGJ1ZmYgMzIpKSkKICAgIChiZWdpbgogICAgICAgIDs7IGNoZWNrIG5vIGJsb2NrIGhhcyBiZWVuIGNvbW1pdHRlZCBhdCB0aGlzIGhlaWdodAogICAgICAgIChhc3NlcnRzISAoaXMtbm9uZSAobWFwLWdldD8gYmxvY2stY29tbWl0cyBjb21taXQtYmxvY2staGVpZ2h0KSkgKGVyciBFUlJfQkxPQ0tfQUxSRUFEWV9DT01NSVRURUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IGB0YXJnZXQtY2hhaW4tdGlwYCBtYXRjaGVzIHRoZSBidXJuIGNoYWluIHRpcAogICAgICAgIChhc3NlcnRzISAoaXMtZXEKICAgICAgICAgICAgdGFyZ2V0LWNoYWluLXRpcAogICAgICAgICAgICAodW53cmFwISAoZ2V0LWJsb2NrLWluZm8/IGlkLWhlYWRlci1oYXNoICgtIGJsb2NrLWhlaWdodCB1MSkpIChlcnIgRVJSX0NBTExFRF9UT09fRUFSTFkpKSApCiAgICAgICAgICAgIChlcnIgRVJSX0lOVkFMSURfQ0hBSU5fVElQKSkKCiAgICAgICAgOzsgY2hlY2sgdGhhdCB0aGUgdHggc2VuZGVyIGlzIG9uZSBvZiB0aGUgbWluZXJzCiAgICAgICAgKGFzc2VydHMhIChpcy1taW5lciB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSBtaW5lciBjYWxsZWQgdGhpcyBjb250cmFjdCBkaXJlY3RseQogICAgICAgIChhc3NlcnRzISAoaXMtbWluZXIgY29udHJhY3QtY2FsbGVyKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEhlbHBlciBmdW5jdGlvbjogbW9kaWZpZXMgdGhlIGJsb2NrLWNvbW1pdHMgbWFwIHdpdGggYSBuZXcgY29tbWl0IGFuZCBwcmludHMgcmVsYXRlZCBpbmZvCjs7IFJldHVybnMgcmVzcG9uc2U8KGJ1ZmYgMzIpLCA/PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLWNvbW1pdC1ibG9jawogICAgICAgIChibG9jayAoYnVmZiAzMikpCiAgICAgICAgKGNvbW1pdC1ibG9jay1oZWlnaHQgdWludCkKICAgICAgICAodGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICApCiAgICAoYmVnaW4KICAgICAgICAobWFwLXNldCBibG9jay1jb21taXRzIGNvbW1pdC1ibG9jay1oZWlnaHQgYmxvY2spCiAgICAgICAgKG1hcC1zZXQgd2l0aGRyYXdhbC1yb290cy1tYXAgd2l0aGRyYXdhbC1yb290IHRydWUpCiAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgZXZlbnQ6ICJibG9jay1jb21taXQiLAogICAgICAgICAgICBibG9jay1jb21taXQ6IGJsb2NrLAogICAgICAgICAgICBibG9jay1oZWlnaHQ6IGNvbW1pdC1ibG9jay1oZWlnaHQsCiAgICAgICAgICAgIHdpdGhkcmF3YWwtcm9vdDogd2l0aGRyYXdhbC1yb290LAogICAgICAgICAgICB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQ6IHRhcmdldC1idXJuLWJsb2NrLWhlaWdodAogICAgICAgIH0pCiAgICAgICAgKG9rIGJsb2NrKQogICAgKQopCgo7OyBUaGUgc3VibmV0IG1pbmVyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gY29tbWl0IGEgYmxvY2sgYXQgYSBwYXJ0aWN1bGFyIGhlaWdodC4KOzsgYGJsb2NrYCBpcyB0aGUgaGFzaCBvZiB0aGUgYmxvY2sgYmVpbmcgc3VibWl0dGVkLgo7OyBgdGFyZ2V0LWNoYWluLXRpcGAgaXMgdGhlIGBpZC1oZWFkZXItaGFzaGAgb2YgdGhlIGJ1cm4gYmxvY2sgKGkuZS4sIGJsb2NrIG9uCjs7ICAgIHRoaXMgY2hhaW4pIHRoYXQgdGhlIG1pbmVyIGludGVuZHMgdG8gYnVpbGQgb2ZmLgo7Owo7OyBGYWlscyBpZjoKOzsgIDEpIHdlIGhhdmUgYWxyZWFkeSBjb21taXR0ZWQgYXQgdGhpcyBibG9jayBoZWlnaHQKOzsgIDIpIGB0YXJnZXQtY2hhaW4tdGlwYCBpcyBub3QgdGhlIGJ1cm4gY2hhaW4gdGlwIChpLmUuLCBvbiB0aGlzIGNoYWluKQo7OyAgMykgdGhlIHNlbmRlciBpcyBub3QgYSBtaW5lcgooZGVmaW5lLXB1YmxpYyAoY29tbWl0LWJsb2NrCiAgICAgICAgKGJsb2NrIChidWZmIDMyKSkKICAgICAgICAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KQogICAgICAgICh0YXJnZXQtY2hhaW4tdGlwIChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKCh0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgYmxvY2staGVpZ2h0KSkKICAgICAgICAodHJ5ISAoY2FuLWNvbW1pdC1ibG9jaz8gdGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHRhcmdldC1jaGFpbi10aXApKQogICAgICAgIChpbm5lci1jb21taXQtYmxvY2sgYmxvY2sgY29tbWl0LWJsb2NrLWhlaWdodCB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgd2l0aGRyYXdhbC1yb290KQogICAgKQopCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBORlQgQVNTRVQgVFJBTlNGRVJTCgo7OyBIZWxwZXIgZnVuY3Rpb24gdGhhdCB0cmFuc2ZlcnMgdGhlIHNwZWNpZmllZCBORlQgZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgdHJhbnNmZXIgaWQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChhcy1jb250cmFjdCAoY29udHJhY3QtY2FsbD8gbmZ0LW1pbnQtY29udHJhY3QgbWludC1mcm9tLXN1Ym5ldCBpZCBzZW5kZXIgcmVjaXBpZW50KSkpCiAgICAgICAgICAgIChtaW50LXJlc3VsdCAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICkKICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB0cmFuc2ZlciBzdWNjZWVkZWQKICAgICAgICAoYXNzZXJ0cyEgbWludC1yZXN1bHQgKGVyciBFUlJfTUlOVF9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikKICAgICAgICAoaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGNvbnRyYWN0LWNhbGw/IG5mdC1jb250cmFjdCBnZXQtb3duZXIgaWQpKQogICAgICAgICAgICAobmZ0LW93bmVyICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLW5mdCAoaXMtZXEgbmZ0LW93bmVyIChzb21lIENPTlRSQUNUX0FERFJFU1MpKSkKICAgICAgICAgICAgKG5vLW93bmVyIChpcy1lcSBuZnQtb3duZXIgbm9uZSkpCiAgICAgICAgKQoKICAgICAgICAoaWYgY29udHJhY3Qtb3ducy1uZnQKICAgICAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgIChpZiBuby1vd25lcgogICAgICAgICAgICAgICAgOzsgVHJ5IG1pbnRpbmcgdGhlIGFzc2V0IGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG93bmVyIG9mIHRoaXMgTkZUCiAgICAgICAgICAgICAgICAoaW5uZXItbWludC1uZnQtYXNzZXQgbmZ0LW1pbnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgICAgICA7OyBJbiB0aGlzIGNhc2UsIGEgcHJpbmNpcGFsIG90aGVyIHRoYW4gdGhpcyBjb250cmFjdCBvd25zIHRoaXMgTkZULCBzbyBtaW50aW5nIGlzIG5vdCBwb3NzaWJsZQogICAgICAgICAgICAgICAgKGVyciBFUlJfTUlOVF9GQUlMRUQpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgYW4gTkZUIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQoKICAgICAgICA7OyBUcnkgdG8gdHJhbnNmZXIgdGhlIE5GVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1uZnQtYXNzZXQgbmZ0LWNvbnRyYWN0IGlkIHNlbmRlciBDT05UUkFDVF9BRERSRVNTKSkgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgOzsgRW1pdCBhIHByaW50IGV2ZW50IC0gdGhlIG5vZGUgY29uc3VtZXMgdGhpcwogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1uZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IG5mdC1jb250cmFjdCksCiAgICAgICAgICAgIG5mdC1pZDogaWQsCiAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICBzdWJuZXQtY29udHJhY3QtaWQ6IHN1Ym5ldC1jb250cmFjdC1pZCwKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IEhlbHBlciBmdW5jdGlvbiBmb3IgYHdpdGhkcmF3LW5mdC1hc3NldGAKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChpbm5lci13aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChsMi1jb250cmFjdCBwcmluY2lwYWwpCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAobGV0ICgoaGFzaGVzLWFyZS12YWxpZCAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMgd2l0aGRyYXdhbC1yb290IHdpdGhkcmF3YWwtbGVhZi1oYXNoIHNpYmxpbmctaGFzaGVzKSkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISBoYXNoZXMtYXJlLXZhbGlkKSAoZXJyIEVSUl9WQUxJREFUSU9OX0ZBSUxFRCkpCgogICAgICAgIDs7IGNoZWNrIHRoYXQgdGhlIHdpdGhkcmF3YWwgcmVxdWVzdCBkYXRhIG1hdGNoZXMgdGhlIHN1cHBsaWVkIGxlYWYgaGFzaAogICAgICAgIChhc3NlcnRzISAoaXMtZXEgd2l0aGRyYXdhbC1sZWFmLWhhc2gKICAgICAgICAgICAgICAgICAgICAgICAgIChsZWFmLWhhc2gtd2l0aGRyYXctbmZ0IGwyLWNvbnRyYWN0IGlkIHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBuZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgICAgIG1pbnQtY29udHJhY3QgKGFzLWNvbnRyYWN0IChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgbWludC1jb250cmFjdCBpZCByZWNpcGllbnQpKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgcmVjaXBpZW50KSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKQogICAgICAgICkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIHdpdGhkcmF3IHRoZSBzcGVjaWZpZWQgTkZUIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChpZCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChuZnQtbWludC1jb250cmFjdCAob3B0aW9uYWwgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIGFzc2V0IGJlbG9uZ3MgdG8gdGhlIGFsbG93ZWQtY29udHJhY3RzIG1hcAogICAgICAgICAgICAobDItY29udHJhY3QgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgICAodHJ5ISAoaW5uZXItd2l0aGRyYXctbmZ0LWFzc2V0CiAgICAgICAgICAgICAgICBuZnQtY29udHJhY3QKICAgICAgICAgICAgICAgIGwyLWNvbnRyYWN0CiAgICAgICAgICAgICAgICBpZAogICAgICAgICAgICAgICAgcmVjaXBpZW50CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWlkCiAgICAgICAgICAgICAgICBoZWlnaHQKICAgICAgICAgICAgICAgIG5mdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLXJvb3QKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICBzaWJsaW5nLWhhc2hlcwogICAgICAgICAgICApKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgewogICAgICAgICAgICBldmVudDogIndpdGhkcmF3LW5mdCIsCiAgICAgICAgICAgIGwxLWNvbnRyYWN0LWlkOiAoYXMtY29udHJhY3QgbmZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbmZ0LWlkOiBpZCwKICAgICAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IExpa2UgYGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtbmZ0LWFzc2V0IGJ1dCB3aXRob3V0IGFsbG93aW5nIG9yIHJlcXVpcmluZyBhIG1pbnQgZnVuY3Rpb24uIEluIG9yZGVyIHRvIHdpdGhkcmF3LCB0aGUgdXNlciBtdXN0Cjs7IGhhdmUgdGhlIGFwcHJvcHJpYXRlIGJhbGFuY2UuCihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgZ2V0LW93bmVyIGlkKSkKICAgICAgICAgICAgKG5mdC1vd25lciAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICAoY29udHJhY3Qtb3ducy1uZnQgKGlzLWVxIG5mdC1vd25lciAoc29tZSBDT05UUkFDVF9BRERSRVNTKSkpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEgY29udHJhY3Qtb3ducy1uZnQgKGVyciBFUlJfTkZUX05PVF9PV05FRF9CWV9DT05UUkFDVCkpCiAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICApCikKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgRk9SIEZVTkdJQkxFIFRPS0VOIEFTU0VUIFRSQU5TRkVSUwoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aGUgZnVuZ2libGUgdG9rZW4gZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChzZW5kZXIgcHJpbmNpcGFsKQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgdHJhbnNmZXIgYW1vdW50IHNlbmRlciByZWNpcGllbnQgbWVtbykpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgRklYTUU6IFNJUC0wMTAgZG9lc24ndCByZXF1aXJlIHRoYXQgdHJhbnNmZXIgcmV0dXJucyAob2sgdHJ1ZSkgb24gc3VjY2Vzcywgc28gaXMgdGhpcyBjaGVjayBuZWNlc3Nhcnk/CiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LWZ0LWFzc2V0CiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAoc2VuZGVyIHByaW5jaXBhbCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGFzLWNvbnRyYWN0IChjb250cmFjdC1jYWxsPyBmdC1taW50LWNvbnRyYWN0IG1pbnQtZnJvbS1zdWJuZXQgYW1vdW50IHNlbmRlciByZWNpcGllbnQpKSkKICAgICAgICAgICAgKG1pbnQtcmVzdWx0ICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISBtaW50LXJlc3VsdCAoZXJyIEVSUl9NSU5UX0ZBSUxFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgZ2V0LWJhbGFuY2UgQ09OVFJBQ1RfQUREUkVTUykpCiAgICAgICAgICAgIChjb250cmFjdC1mdC1iYWxhbmNlICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLWVub3VnaCAoPj0gY29udHJhY3QtZnQtYmFsYW5jZSBhbW91bnQpKQogICAgICAgICAgICAoYW1vdW50LXRvLXRyYW5zZmVyIChpZiBjb250cmFjdC1vd25zLWVub3VnaCBhbW91bnQgY29udHJhY3QtZnQtYmFsYW5jZSkpCiAgICAgICAgICAgIChhbW91bnQtdG8tbWludCAoLSBhbW91bnQgYW1vdW50LXRvLXRyYW5zZmVyKSkKICAgICAgICApCgogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRvdGFsIGJhbGFuY2UgYmV0d2VlbiB0aGUgdHJhbnNmZXIgYW5kIG1pbnQgaXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIGJhbGFuY2UKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIGFtb3VudCAoKyBhbW91bnQtdG8tdHJhbnNmZXIgYW1vdW50LXRvLW1pbnQpKSAoZXJyIEVSUl9JTl9DT01QVVRBVElPTikpCgogICAgICAgIChhbmQKICAgICAgICAgICAgKD4gYW1vdW50LXRvLXRyYW5zZmVyIHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50LXRvLXRyYW5zZmVyIENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICkKICAgICAgICAoYW5kCiAgICAgICAgICAgICg+IGFtb3VudC10by1taW50IHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItbWludC1mdC1hc3NldCBmdC1taW50LWNvbnRyYWN0IGFtb3VudC10by1taW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50KSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FsbHMgdGhpcyBmdW5jdGlvbiB0byBkZXBvc2l0IGEgZnVuZ2libGUgdG9rZW4gaW50byB0aGUgY29udHJhY3QuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKGRlcG9zaXQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKSkKICAgICAgICApCiAgICAgICAgOzsgVHJ5IHRvIHRyYW5zZmVyIHRoZSBGVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1mdC1hc3NldCBmdC1jb250cmFjdCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MgbWVtbykpIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkpCgogICAgICAgIChsZXQgKAogICAgICAgICAgICAgICAgKGZ0LW5hbWUgKHVud3JhcCEgKGNvbnRyYWN0LWNhbGw/IGZ0LWNvbnRyYWN0IGdldC1uYW1lKSAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICApCiAgICAgICAgICAgIDs7IEVtaXQgYSBwcmludCBldmVudCAtIHRoZSBub2RlIGNvbnN1bWVzIHRoaXMKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1mdCIsCiAgICAgICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgICAgIGZ0LW5hbWU6IGZ0LW5hbWUsCiAgICAgICAgICAgICAgICBmdC1hbW91bnQ6IGFtb3VudCwKICAgICAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICAgICAgc3VibmV0LWNvbnRyYWN0LWlkOiBzdWJuZXQtY29udHJhY3QtaWQsCiAgICAgICAgICAgIH0pCiAgICAgICAgKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBwZXJmb3JtcyB2YWxpZGl0eSBjaGVja3MgcmVsYXRlZCB0byB0aGUgd2l0aGRyYXdhbCBhbmQgcGVyZm9ybXMgdGhlIHdpdGhkcmF3YWwgYXMgd2VsbC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItd2l0aGRyYXctZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgKG9wdGlvbmFsIDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgaGFzaGVzLWFyZS12YWxpZCkgKGVyciBFUlJfVkFMSURBVElPTl9GQUlMRUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LWZ0IChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgYW1vdW50IHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBmdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICAgICAgbWludC1jb250cmFjdCAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQgZnQtY29udHJhY3QgbWludC1jb250cmFjdCBhbW91bnQgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICAgICAgICAgIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkKICAgICAgICApCgogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgKGVyciBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB3aXRoZHJhdyBzb21lIGFtb3VudCBvZiBhIGZ1bmdpYmxlIHRva2VuIGFzc2V0IGZyb20gdGhlCjs7IGNvbnRyYWN0IGFuZCBzZW5kIGl0IHRvIGEgcmVjaXBpZW50Lgo7OyBJbiBvcmRlciBmb3IgdGhpcyB3aXRoZHJhd2FsIHRvIGdvIHRocm91Z2gsIHRoZSBnaXZlbiB3aXRoZHJhd2FsIG11c3QgaGF2ZSBiZWVuIGluY2x1ZGVkCjs7IGluIGEgd2l0aGRyYXdhbCBNZXJrbGUgdHJlZSBhIHN1Ym5ldCBtaW5lciBzdWJtaXR0ZWQuIFRoZSB1c2VyIG11c3QgcHJvdmlkZSB0aGUgbGVhZgo7OyBoYXNoIG9mIHRoZWlyIHdpdGhkcmF3YWwgYW5kIHRoZSByb290IGhhc2ggb2YgdGhlIHNwZWNpZmljIE1lcmtsZSB0cmVlIHRoZWlyIHdpdGhkcmF3YWwKOzsgaXMgaW5jbHVkZWQgaW4uIFRoZXkgbXVzdCBhbHNvIHByb3ZpZGUgYSBsaXN0IG9mIHNpYmxpbmcgaGFzaGVzLiBUaGUgd2l0aGRyYXcgZnVuY3Rpb24KOzsgdXNlcyB0aGUgcHJvdmlkZWQgaGFzaGVzIHRvIGVuc3VyZSB0aGUgcmVxdWVzdGVkIHdpdGhkcmF3YWwgaXMgdmFsaWQuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKHdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgICAgIChmdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAoYmVnaW4KICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB3aXRoZHJhdyBhbW91bnQgaXMgcG9zaXRpdmUKICAgICAgICAoYXNzZXJ0cyEgKD4gYW1vdW50IHUwKSAoZXJyIEVSUl9BVFRFTVBUX1RPX1RSQU5TRkVSX1pFUk9fQU1PVU5UKSkKCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEgKGlubmVyLXdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgICAgICAgICBmdC1jb250cmFjdAogICAgICAgICAgICAgICAgYW1vdW50CiAgICAgICAgICAgICAgICByZWNpcGllbnQKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtaWQKICAgICAgICAgICAgICAgIGhlaWdodAogICAgICAgICAgICAgICAgbWVtbwogICAgICAgICAgICAgICAgZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWxlYWYtaGFzaAogICAgICAgICAgICAgICAgc2libGluZy1oYXNoZXMpKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAobGV0ICgKICAgICAgICAgICAgICAgIChmdC1uYW1lICh1bndyYXAhIChjb250cmFjdC1jYWxsPyBmdC1jb250cmFjdCBnZXQtbmFtZSkgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICAgICAgKQogICAgICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAid2l0aGRyYXctZnQiLAogICAgICAgICAgICAgICAgbDEtY29udHJhY3QtaWQ6IChhcy1jb250cmFjdCBmdC1jb250cmFjdCksCiAgICAgICAgICAgICAgICBmdC1uYW1lOiBmdC1uYW1lLAogICAgICAgICAgICAgICAgZnQtYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgfSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgoKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBTVFggVFJBTlNGRVJTCgoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIHRoZSBnaXZlbiBhbW91bnQgZnJvbSB0aGUgc3BlY2lmaWVkIGZ1bmdpYmxlIHRva2VuIGZyb20gdGhlIGdpdmVuIHNlbmRlciB0byB0aGUgZ2l2ZW4gcmVjaXBpZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1zdHggKGFtb3VudCB1aW50KSAoc2VuZGVyIHByaW5jaXBhbCkgKHJlY2lwaWVudCBwcmluY2lwYWwpKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoc3R4LXRyYW5zZmVyPyBhbW91bnQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISB0cmFuc2Zlci1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgU1RYIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LXN0eCAoYW1vdW50IHVpbnQpIChzZW5kZXIgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFRyeSB0byB0cmFuc2ZlciB0aGUgU1RYIHRvIHRoaXMgY29udHJhY3QKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQgLSB0aGUgbm9kZSBjb25zdW1lcyB0aGlzCiAgICAgICAgKHByaW50IHsgZXZlbnQ6ICJkZXBvc2l0LXN0eCIsIHNlbmRlcjogc2VuZGVyLCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICApCiAgICAoc2hhNTEyLzI1NiAoY29uY2F0IDB4MDAgKHVud3JhcC1wYW5pYyAodG8tY29uc2Vuc3VzLWJ1ZmY/CiAgICAgICAgewogICAgICAgICAgICB0eXBlOiAic3R4IiwKICAgICAgICAgICAgYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1uZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChuZnQtaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJuZnQiLAogICAgICAgICAgICBuZnQtaWQ6IG5mdC1pZCwKICAgICAgICAgICAgYXNzZXQtY29udHJhY3Q6IGFzc2V0LWNvbnRyYWN0LAogICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgd2l0aGRyYXdhbC1pZDogd2l0aGRyYXdhbC1pZCwKICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQKICAgICAgICB9KSkpCiAgICApCikKCihkZWZpbmUtcmVhZC1vbmx5IChsZWFmLWhhc2gtd2l0aGRyYXctZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJmdCIsCiAgICAgICAgICAgIGFtb3VudDogYW1vdW50LAogICAgICAgICAgICBhc3NldC1jb250cmFjdDogYXNzZXQtY29udHJhY3QsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKOzsgQSB1c2VyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gd2l0aGRyYXcgU1RYIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKCiAgICAgICAgKGFzc2VydHMhICh0cnkhIGhhc2hlcy1hcmUtdmFsaWQpIChlcnIgRVJSX1ZBTElEQVRJT05fRkFJTEVEKSkKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LXN0eCBhbW91bnQgcmVjaXBpZW50IHdpdGhkcmF3YWwtaWQgaGVpZ2h0KSkKICAgICAgICAgICAgICAgICAgKGVyciBFUlJfVkFMSURBVElPTl9MRUFGX0ZBSUxFRCkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgdHgtc2VuZGVyIHJlY2lwaWVudCkpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgIChmaW5pc2gtd2l0aGRyYXcgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogd2l0aGRyYXdhbC1sZWFmLWhhc2gsIHdpdGhkcmF3YWwtcm9vdC1oYXNoOiB3aXRoZHJhd2FsLXJvb3QgfSkKICAgICAgICAgIChlcnIgRVJSX1dJVEhEUkFXQUxfQUxSRUFEWV9QUk9DRVNTRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgeyBldmVudDogIndpdGhkcmF3LXN0eCIsIHJlY2lwaWVudDogcmVjaXBpZW50LCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBHRU5FUkFMIFdJVEhEUkFXQUwgRlVOQ1RJT05TCgo7OyBUaGlzIGZ1bmN0aW9uIGNvbmNhdHMgdGhlIHR3byBnaXZlbiBoYXNoZXMgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuIEl0IGFsc28gcHJlcGVuZHMgdGhlIGJ1ZmYgYDB4MDFgLCB3aGljaCBpcwo7OyBhIHRhZyBkZW5vdGluZyBhIG5vZGUgKHZlcnN1cyBhIGxlYWYpLgo7OyBSZXR1cm5zIGEgYnVmZgooZGVmaW5lLXByaXZhdGUgKGNyZWF0ZS1ub2RlLWhhc2gKICAgICAgICAoY3Vyci1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoIChidWZmIDMyKSkKICAgICAgICAoaXMtc2libGluZy1sZWZ0LXNpZGUgYm9vbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY29uY2F0dGVkLWhhc2ggKGlmIGlzLXNpYmxpbmctbGVmdC1zaWRlCiAgICAgICAgICAgICAgICAgICAgKGNvbmNhdCBzaWJsaW5nLWhhc2ggY3Vyci1oYXNoKQogICAgICAgICAgICAgICAgICAgIChjb25jYXQgY3Vyci1oYXNoIHNpYmxpbmctaGFzaCkKICAgICAgICAgICAgICAgICkpCiAgICAgICAgICApCgogICAgICAgICAgKGNvbmNhdCAweDAxIGNvbmNhdHRlZC1oYXNoKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIGhhc2hlcyB0aGUgY3VyciBoYXNoIHdpdGggaXRzIHNpYmxpbmcgaGFzaC4KOzsgUmV0dXJucyAoYnVmZiAzMikKKGRlZmluZS1wcml2YXRlIChoYXNoLWhlbHAKICAgICAgICAoc2libGluZyB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pCiAgICAgICAgKGN1cnItbm9kZS1oYXNoIChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoc2libGluZy1oYXNoIChnZXQgaGFzaCBzaWJsaW5nKSkKICAgICAgICAgICAgKGlzLXNpYmxpbmctbGVmdC1zaWRlIChnZXQgaXMtbGVmdC1zaWRlIHNpYmxpbmcpKQogICAgICAgICAgICAobmV3LWJ1ZmYgKGNyZWF0ZS1ub2RlLWhhc2ggY3Vyci1ub2RlLWhhc2ggc2libGluZy1oYXNoIGlzLXNpYmxpbmctbGVmdC1zaWRlKSkKICAgICAgICApCiAgICAgICAoc2hhNTEyLzI1NiBuZXctYnVmZikKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBjaGVja3M6Cjs7ICAtIFRoYXQgdGhlIHByb3ZpZGVkIHdpdGhkcmF3YWwgcm9vdCBtYXRjaGVzIGEgcHJldmlvdXNseSBzdWJtaXR0ZWQgb25lIChwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uIGBjb21taXQtYmxvY2tgKQo7OyAgLSBUaGF0IHRoZSBjb21wdXRlZCB3aXRoZHJhd2FsIHJvb3QgbWF0Y2hlcyBhIHByZXZpb3VzIHZhbGlkIHdpdGhkcmF3YWwgcm9vdAo7OyAgLSBUaGF0IHRoZSBnaXZlbiB3aXRoZHJhd2FsIGxlYWYgaGFzaCBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBwcm9jZXNzZWQKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGJlZ2luCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdXNlciBzdWJtaXR0ZWQgYSB2YWxpZCB3aXRoZHJhd2FsIHJvb3QKICAgICAgICAoYXNzZXJ0cyEgKGlzLXNvbWUgKG1hcC1nZXQ/IHdpdGhkcmF3YWwtcm9vdHMtbWFwIHdpdGhkcmF3YWwtcm9vdCkpIChlcnIgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QpKQoKICAgICAgICA7OyBDaGVjayB0aGF0IHRoaXMgd2l0aGRyYXdhbCBsZWFmIGhhcyBub3QgYmVlbiBwcm9jZXNzZWQgYmVmb3JlCiAgICAgICAgKGFzc2VydHMhCiAgICAgICAgICAgIChpcy1ub25lCiAgICAgICAgICAgICAobWFwLWdldD8gcHJvY2Vzc2VkLXdpdGhkcmF3YWwtbGVhdmVzLW1hcAogICAgICAgICAgICAgICAgICAgICAgIHsgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IHdpdGhkcmF3YWwtbGVhZi1oYXNoLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogd2l0aGRyYXdhbC1yb290IH0pKQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKSkKCiAgICAgICAgKGxldCAoKGNhbGN1bGF0ZWQtd2l0aGRyYXdhbC1yb290IChmb2xkIGhhc2gtaGVscCBzaWJsaW5nLWhhc2hlcyB3aXRoZHJhd2FsLWxlYWYtaGFzaCkpCiAgICAgICAgICAgICAgKHJvb3RzLW1hdGNoIChpcy1lcSBjYWxjdWxhdGVkLXdpdGhkcmF3YWwtcm9vdCB3aXRoZHJhd2FsLXJvb3QpKSkKICAgICAgICAgICAgIChpZiByb290cy1tYXRjaAogICAgICAgICAgICAgICAgKG9rIHRydWUpCiAgICAgICAgICAgICAgICAoZXJyIEVSUl9NRVJLTEVfUk9PVF9ET0VTX05PVF9NQVRDSCkpCiAgICAgICAgKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBjYWxsZWQgYWZ0ZXIgdGhlIGFzc2V0IGluIHF1ZXN0aW9uIGhhcyBiZWVuIHRyYW5zZmVycmVkLgo7OyBJdCBhZGRzIHRoZSB3aXRoZHJhd2FsIGxlYWYgaGFzaCB0byBhIG1hcCBvZiBwcm9jZXNzZWQgbGVhdmVzLiBUaGlzIGVuc3VyZXMgdGhhdAo7OyB0aGlzIHdpdGhkcmF3YWwgbGVhZiBjYW4ndCBiZSB1c2VkIGFnYWluIHRvIHdpdGhkcmF3IGFkZGl0aW9uYWwgZnVuZHMuCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGZpbmlzaC13aXRoZHJhdwogICAgICAgICh3aXRoZHJhdy1pbmZvIHsKICAgICAgICAgICAgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290LWhhc2g6IChidWZmIDMyKQogICAgICAgIH0pCiAgICApCiAgICAobWFwLWluc2VydCBwcm9jZXNzZWQtd2l0aGRyYXdhbC1sZWF2ZXMtbWFwIHdpdGhkcmF3LWluZm8gdHJ1ZSkKKQo=", - "clarity_version": "Clarity2", - "cost": 290960, - "location": { - "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2.clar" - } - } + "source": "KGRlZmluZS10cmFpdCBzaXAtMDEwLXRyYWl0CiAgKAogICAgOzsgVHJhbnNmZXIgZnJvbSB0aGUgY2FsbGVyIHRvIGEgbmV3IHByaW5jaXBhbAogICAgKHRyYW5zZmVyICh1aW50IHByaW5jaXBhbCBwcmluY2lwYWwgKG9wdGlvbmFsIChidWZmIDM0KSkpIChyZXNwb25zZSBib29sIHVpbnQpKQoKICAgIDs7IHRoZSBodW1hbiByZWFkYWJsZSBuYW1lIG9mIHRoZSB0b2tlbgogICAgKGdldC1uYW1lICgpIChyZXNwb25zZSAoc3RyaW5nLWFzY2lpIDMyKSB1aW50KSkKCiAgICA7OyB0aGUgdGlja2VyIHN5bWJvbCwgb3IgZW1wdHkgaWYgbm9uZQogICAgKGdldC1zeW1ib2wgKCkgKHJlc3BvbnNlIChzdHJpbmctYXNjaWkgMzIpIHVpbnQpKQoKICAgIDs7IHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdXNlZCwgZS5nLiA2IHdvdWxkIG1lYW4gMV8wMDBfMDAwIHJlcHJlc2VudHMgMSB0b2tlbgogICAgKGdldC1kZWNpbWFscyAoKSAocmVzcG9uc2UgdWludCB1aW50KSkKCiAgICA7OyB0aGUgYmFsYW5jZSBvZiB0aGUgcGFzc2VkIHByaW5jaXBhbAogICAgKGdldC1iYWxhbmNlIChwcmluY2lwYWwpIChyZXNwb25zZSB1aW50IHVpbnQpKQoKICAgIDs7IHRoZSBjdXJyZW50IHRvdGFsIHN1cHBseSAod2hpY2ggZG9lcyBub3QgbmVlZCB0byBiZSBhIGNvbnN0YW50KQogICAgKGdldC10b3RhbC1zdXBwbHkgKCkgKHJlc3BvbnNlIHVpbnQgdWludCkpCgogICAgOzsgYW4gb3B0aW9uYWwgVVJJIHRoYXQgcmVwcmVzZW50cyBtZXRhZGF0YSBvZiB0aGlzIHRva2VuCiAgICAoZ2V0LXRva2VuLXVyaSAoKSAocmVzcG9uc2UgKG9wdGlvbmFsIChzdHJpbmctdXRmOCAyNTYpKSB1aW50KSkKICApCik=", + "clarity_version": 1, + "cost": 8390, + "location": { + "path": "./.cache/requirements/ST1NXBK3K5YYMD6FD41MVNP3JS1GABZ8TRVX023PT.sip-010-trait-ft-standard.clar" + } + } + ], + "epoch": "2.05" + }, + { + "id": 2, + "transactions": [ + { + "transaction_type": "RequirementPublish", + "contract_id": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" }, - { - "ContractPublish": { - "contract_name": "px", - "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "location": { - "path": "contracts/px.clar" - }, - "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", - "clarity_version": "Clarity2", - "cost": 18060, - "anchor_block_only": true - } + "source": "OzsgSW4gb3JkZXIgdG8gc3VwcG9ydCB3aXRoZHJhd2luZyBhbiBhc3NldCB0aGF0IHdhcyBtaW50ZWQgb24gYSBzdWJuZXQsIHRoZQo7OyBMMSBjb250cmFjdCBtdXN0IGltcGxlbWVudCB0aGlzIHRyYWl0LgooZGVmaW5lLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQKICAoCiAgICA7OyBQcm9jZXNzIGEgd2l0aGRyYXdhbCBmcm9tIHRoZSBzdWJuZXQgZm9yIGFuIGFzc2V0IHdoaWNoIGRvZXMgbm90IHlldAogICAgOzsgZXhpc3Qgb24gdGhpcyBuZXR3b3JrLCBhbmQgdGh1cyByZXF1aXJlcyBhIG1pbnQuCiAgICAobWludC1mcm9tLXN1Ym5ldAogICAgICAoCiAgICAgICAgdWludCAgICAgICA7OyBhc3NldC1pZCAoTkZUKSBvciBhbW91bnQgKEZUKQogICAgICAgIHByaW5jaXBhbCAgOzsgc2VuZGVyCiAgICAgICAgcHJpbmNpcGFsICA7OyByZWNpcGllbnQKICAgICAgKQogICAgICAocmVzcG9uc2UgYm9vbCB1aW50KQogICAgKQogICkKKQ==", + "clarity_version": 2, + "cost": 4810, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-traits-v1.clar" } - ], - "epoch": "2.1" - }, - { - "id": 3, - "transactions": [ - { - "ContractCall": { - "contract_id": { - "issuer": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "name": "px" - }, - "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - "method": "set-value-at", - "parameters": ["u0", "0xfffffa"], - "cost": 2240, - "anchor_block_only": false - } + }, + { + "transaction_type": "RequirementPublish", + "contract_id": "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2", + "remap_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "remap_principals": { + "ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" }, - { - "StxTransfer": { - "expected_sender": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", - "recipient": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", - "mstx_amount": 200, - "memo": "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", - "cost": 2240, - "anchor_block_only": true - } + "source": "OzsgVGhlIC5zdWJuZXQgY29udHJhY3QKCihkZWZpbmUtY29uc3RhbnQgQ09OVFJBQ1RfQUREUkVTUyAoYXMtY29udHJhY3QgdHgtc2VuZGVyKSkKCjs7IEVycm9yIGNvZGVzCihkZWZpbmUtY29uc3RhbnQgRVJSX0JMT0NLX0FMUkVBRFlfQ09NTUlUVEVEIDEpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUlORVIgMikKKGRlZmluZS1jb25zdGFudCBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQgMykKKGRlZmluZS1jb25zdGFudCBFUlJfVFJBTlNGRVJfRkFJTEVEIDQpCihkZWZpbmUtY29uc3RhbnQgRVJSX0RJU0FMTE9XRURfQVNTRVQgNSkKKGRlZmluZS1jb25zdGFudCBFUlJfQVNTRVRfQUxSRUFEWV9BTExPV0VEIDYpCihkZWZpbmUtY29uc3RhbnQgRVJSX01FUktMRV9ST09UX0RPRVNfTk9UX01BVENIIDcpCihkZWZpbmUtY29uc3RhbnQgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QgOCkKKGRlZmluZS1jb25zdGFudCBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCA5KQooZGVmaW5lLWNvbnN0YW50IEVSUl9WQUxJREFUSU9OX0ZBSUxFRCAxMCkKOzs7IFRoZSB2YWx1ZSBzdXBwbGllZCBmb3IgYHRhcmdldC1jaGFpbi10aXBgIGRvZXMgbm90IG1hdGNoIHRoZSBjdXJyZW50IGNoYWluIHRpcC4KKGRlZmluZS1jb25zdGFudCBFUlJfSU5WQUxJRF9DSEFJTl9USVAgMTEpCjs7OyBUaGUgY29udHJhY3Qgd2FzIGNhbGxlZCBiZWZvcmUgcmVhY2hpbmcgdGhpcy1jaGFpbiBoZWlnaHQgcmVhY2hlcyAxLgooZGVmaW5lLWNvbnN0YW50IEVSUl9DQUxMRURfVE9PX0VBUkxZIDEyKQooZGVmaW5lLWNvbnN0YW50IEVSUl9NSU5UX0ZBSUxFRCAxMykKKGRlZmluZS1jb25zdGFudCBFUlJfQVRURU1QVF9UT19UUkFOU0ZFUl9aRVJPX0FNT1VOVCAxNCkKKGRlZmluZS1jb25zdGFudCBFUlJfSU5fQ09NUFVUQVRJT04gMTUpCjs7IFRoZSBjb250cmFjdCBkb2VzIG5vdCBvd24gdGhpcyBORlQgdG8gd2l0aGRyYXcgaXQuCihkZWZpbmUtY29uc3RhbnQgRVJSX05GVF9OT1RfT1dORURfQllfQ09OVFJBQ1QgMTYpCihkZWZpbmUtY29uc3RhbnQgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQgMzApCgo7OyBNYXAgZnJvbSBTdGFja3MgYmxvY2sgaGVpZ2h0IHRvIGJsb2NrIGNvbW1pdAooZGVmaW5lLW1hcCBibG9jay1jb21taXRzIHVpbnQgKGJ1ZmYgMzIpKQo7OyBNYXAgcmVjb3JkaW5nIHdpdGhkcmF3YWwgcm9vdHMKKGRlZmluZS1tYXAgd2l0aGRyYXdhbC1yb290cy1tYXAgKGJ1ZmYgMzIpIGJvb2wpCjs7IE1hcCByZWNvcmRpbmcgcHJvY2Vzc2VkIHdpdGhkcmF3YWwgbGVhdmVzCihkZWZpbmUtbWFwIHByb2Nlc3NlZC13aXRoZHJhd2FsLWxlYXZlcy1tYXAgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogKGJ1ZmYgMzIpLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogKGJ1ZmYgMzIpIH0gYm9vbCkKCjs7IHByaW5jaXBhbCB0aGF0IGNhbiBjb21taXQgYmxvY2tzCihkZWZpbmUtZGF0YS12YXIgbWluZXIgcHJpbmNpcGFsIHR4LXNlbmRlcikKOzsgcHJpbmNpcGFsIHRoYXQgY2FuIHJlZ2lzdGVyIGNvbnRyYWN0cwooZGVmaW5lLWRhdGEtdmFyIGFkbWluIHByaW5jaXBhbCB0eC1zZW5kZXIpCgo7OyBNYXAgb2YgYWxsb3dlZCBjb250cmFjdHMgZm9yIGFzc2V0IHRyYW5zZmVycyAtIG1hcHMgTDEgY29udHJhY3QgcHJpbmNpcGFsIHRvIEwyIGNvbnRyYWN0IHByaW5jaXBhbAooZGVmaW5lLW1hcCBhbGxvd2VkLWNvbnRyYWN0cyBwcmluY2lwYWwgcHJpbmNpcGFsKQoKOzsgVXNlIHRyYWl0IGRlY2xhcmF0aW9ucwoodXNlLXRyYWl0IG5mdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQubmZ0LXRyYWl0Lm5mdC10cmFpdCkKKHVzZS10cmFpdCBmdC10cmFpdCAnU1QxTlhCSzNLNVlZTUQ2RkQ0MU1WTlAzSlMxR0FCWjhUUlZYMDIzUFQuc2lwLTAxMC10cmFpdC1mdC1zdGFuZGFyZC5zaXAtMDEwLXRyYWl0KQoodXNlLXRyYWl0IG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQgLnN1Ym5ldC10cmFpdHMtdjEubWludC1mcm9tLXN1Ym5ldC10cmFpdCkKCjs7IFVwZGF0ZSB0aGUgbWluZXIgZm9yIHRoaXMgY29udHJhY3QuCihkZWZpbmUtcHVibGljICh1cGRhdGUtbWluZXIgKG5ldy1taW5lciBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgKGFzc2VydHMhIChpcy1lcSB0eC1zZW5kZXIgKHZhci1nZXQgbWluZXIpKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKICAgICAgICAob2sgKHZhci1zZXQgbWluZXIgbmV3LW1pbmVyKSkKICAgICkKKQoKOzsgUmVnaXN0ZXIgYSBuZXcgRlQgY29udHJhY3QgdG8gYmUgc3VwcG9ydGVkIGJ5IHRoaXMgc3VibmV0LgooZGVmaW5lLXB1YmxpYyAocmVnaXN0ZXItbmV3LWZ0LWNvbnRyYWN0IChmdC1jb250cmFjdCA8ZnQtdHJhaXQ+KSAobDItY29udHJhY3QgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFZlcmlmeSB0aGF0IHR4LXNlbmRlciBpcyBhbiBhdXRob3JpemVkIGFkbWluCiAgICAgICAgKGFzc2VydHMhIChpcy1hZG1pbiB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBTZXQgdXAgdGhlIGFzc2V0cyB0aGF0IHRoZSBjb250cmFjdCBpcyBhbGxvd2VkIHRvIHRyYW5zZmVyCiAgICAgICAgKGFzc2VydHMhIChtYXAtaW5zZXJ0IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAiZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdDogKGNvbnRyYWN0LW9mIGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbDItY29udHJhY3Q6IGwyLWNvbnRyYWN0CiAgICAgICAgfSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IFJlZ2lzdGVyIGEgbmV3IE5GVCBjb250cmFjdCB0byBiZSBzdXBwb3J0ZWQgYnkgdGhpcyBzdWJuZXQuCihkZWZpbmUtcHVibGljIChyZWdpc3Rlci1uZXctbmZ0LWNvbnRyYWN0IChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pIChsMi1jb250cmFjdCBwcmluY2lwYWwpKQogICAgKGJlZ2luCiAgICAgICAgOzsgVmVyaWZ5IHRoYXQgdHgtc2VuZGVyIGlzIGFuIGF1dGhvcml6ZWQgYWRtaW4KICAgICAgICAoYXNzZXJ0cyEgKGlzLWFkbWluIHR4LXNlbmRlcikgKGVyciBFUlJfSU5WQUxJRF9NSU5FUikpCgogICAgICAgIDs7IFNldCB1cCB0aGUgYXNzZXRzIHRoYXQgdGhlIGNvbnRyYWN0IGlzIGFsbG93ZWQgdG8gdHJhbnNmZXIKICAgICAgICAoYXNzZXJ0cyEgKG1hcC1pbnNlcnQgYWxsb3dlZC1jb250cmFjdHMgKGNvbnRyYWN0LW9mIG5mdC1jb250cmFjdCkgbDItY29udHJhY3QpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX0FTU0VUX0FMUkVBRFlfQUxMT1dFRCkpCgogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAicmVnaXN0ZXItY29udHJhY3QiLAogICAgICAgICAgICBhc3NldC10eXBlOiAibmZ0IiwKICAgICAgICAgICAgbDEtY29udHJhY3Q6IChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpLAogICAgICAgICAgICBsMi1jb250cmFjdDogbDItY29udHJhY3QKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhIG1pbmVyCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGlzLW1pbmVyIChtaW5lci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIG1pbmVyLXRvLWNoZWNrICh2YXItZ2V0IG1pbmVyKSkKKQoKOzsgSGVscGVyIGZ1bmN0aW9uOiByZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIGdpdmVuIHByaW5jaXBhbCBpcyBhbiBhZG1pbgo7OyBSZXR1cm5zIGJvb2wKKGRlZmluZS1wcml2YXRlIChpcy1hZG1pbiAoYWRkci10by1jaGVjayBwcmluY2lwYWwpKQogICAgKGlzLWVxIGFkZHItdG8tY2hlY2sgKHZhci1nZXQgYWRtaW4pKQopCgo7OyBIZWxwZXIgZnVuY3Rpb246IGRldGVybWluZXMgd2hldGhlciB0aGUgY29tbWl0LWJsb2NrIG9wZXJhdGlvbiBzYXRpc2ZpZXMgcHJlLWNvbmRpdGlvbnMKOzsgbGlzdGVkIGluIGBjb21taXQtYmxvY2tgLgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChjYW4tY29tbWl0LWJsb2NrPyAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KSAgKHRhcmdldC1jaGFpbi10aXAgKGJ1ZmYgMzIpKSkKICAgIChiZWdpbgogICAgICAgIDs7IGNoZWNrIG5vIGJsb2NrIGhhcyBiZWVuIGNvbW1pdHRlZCBhdCB0aGlzIGhlaWdodAogICAgICAgIChhc3NlcnRzISAoaXMtbm9uZSAobWFwLWdldD8gYmxvY2stY29tbWl0cyBjb21taXQtYmxvY2staGVpZ2h0KSkgKGVyciBFUlJfQkxPQ0tfQUxSRUFEWV9DT01NSVRURUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IGB0YXJnZXQtY2hhaW4tdGlwYCBtYXRjaGVzIHRoZSBidXJuIGNoYWluIHRpcAogICAgICAgIChhc3NlcnRzISAoaXMtZXEKICAgICAgICAgICAgdGFyZ2V0LWNoYWluLXRpcAogICAgICAgICAgICAodW53cmFwISAoZ2V0LWJsb2NrLWluZm8/IGlkLWhlYWRlci1oYXNoICgtIGJsb2NrLWhlaWdodCB1MSkpIChlcnIgRVJSX0NBTExFRF9UT09fRUFSTFkpKSApCiAgICAgICAgICAgIChlcnIgRVJSX0lOVkFMSURfQ0hBSU5fVElQKSkKCiAgICAgICAgOzsgY2hlY2sgdGhhdCB0aGUgdHggc2VuZGVyIGlzIG9uZSBvZiB0aGUgbWluZXJzCiAgICAgICAgKGFzc2VydHMhIChpcy1taW5lciB0eC1zZW5kZXIpIChlcnIgRVJSX0lOVkFMSURfTUlORVIpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSBtaW5lciBjYWxsZWQgdGhpcyBjb250cmFjdCBkaXJlY3RseQogICAgICAgIChhc3NlcnRzISAoaXMtbWluZXIgY29udHJhY3QtY2FsbGVyKSAoZXJyIEVSUl9JTlZBTElEX01JTkVSKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEhlbHBlciBmdW5jdGlvbjogbW9kaWZpZXMgdGhlIGJsb2NrLWNvbW1pdHMgbWFwIHdpdGggYSBuZXcgY29tbWl0IGFuZCBwcmludHMgcmVsYXRlZCBpbmZvCjs7IFJldHVybnMgcmVzcG9uc2U8KGJ1ZmYgMzIpLCA/PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLWNvbW1pdC1ibG9jawogICAgICAgIChibG9jayAoYnVmZiAzMikpCiAgICAgICAgKGNvbW1pdC1ibG9jay1oZWlnaHQgdWludCkKICAgICAgICAodGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICApCiAgICAoYmVnaW4KICAgICAgICAobWFwLXNldCBibG9jay1jb21taXRzIGNvbW1pdC1ibG9jay1oZWlnaHQgYmxvY2spCiAgICAgICAgKG1hcC1zZXQgd2l0aGRyYXdhbC1yb290cy1tYXAgd2l0aGRyYXdhbC1yb290IHRydWUpCiAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgZXZlbnQ6ICJibG9jay1jb21taXQiLAogICAgICAgICAgICBibG9jay1jb21taXQ6IGJsb2NrLAogICAgICAgICAgICBibG9jay1oZWlnaHQ6IGNvbW1pdC1ibG9jay1oZWlnaHQsCiAgICAgICAgICAgIHdpdGhkcmF3YWwtcm9vdDogd2l0aGRyYXdhbC1yb290LAogICAgICAgICAgICB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQ6IHRhcmdldC1idXJuLWJsb2NrLWhlaWdodAogICAgICAgIH0pCiAgICAgICAgKG9rIGJsb2NrKQogICAgKQopCgo7OyBUaGUgc3VibmV0IG1pbmVyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gY29tbWl0IGEgYmxvY2sgYXQgYSBwYXJ0aWN1bGFyIGhlaWdodC4KOzsgYGJsb2NrYCBpcyB0aGUgaGFzaCBvZiB0aGUgYmxvY2sgYmVpbmcgc3VibWl0dGVkLgo7OyBgdGFyZ2V0LWNoYWluLXRpcGAgaXMgdGhlIGBpZC1oZWFkZXItaGFzaGAgb2YgdGhlIGJ1cm4gYmxvY2sgKGkuZS4sIGJsb2NrIG9uCjs7ICAgIHRoaXMgY2hhaW4pIHRoYXQgdGhlIG1pbmVyIGludGVuZHMgdG8gYnVpbGQgb2ZmLgo7Owo7OyBGYWlscyBpZjoKOzsgIDEpIHdlIGhhdmUgYWxyZWFkeSBjb21taXR0ZWQgYXQgdGhpcyBibG9jayBoZWlnaHQKOzsgIDIpIGB0YXJnZXQtY2hhaW4tdGlwYCBpcyBub3QgdGhlIGJ1cm4gY2hhaW4gdGlwIChpLmUuLCBvbiB0aGlzIGNoYWluKQo7OyAgMykgdGhlIHNlbmRlciBpcyBub3QgYSBtaW5lcgooZGVmaW5lLXB1YmxpYyAoY29tbWl0LWJsb2NrCiAgICAgICAgKGJsb2NrIChidWZmIDMyKSkKICAgICAgICAoY29tbWl0LWJsb2NrLWhlaWdodCB1aW50KQogICAgICAgICh0YXJnZXQtY2hhaW4tdGlwIChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKCh0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgYmxvY2staGVpZ2h0KSkKICAgICAgICAodHJ5ISAoY2FuLWNvbW1pdC1ibG9jaz8gdGFyZ2V0LWJ1cm4tYmxvY2staGVpZ2h0IHRhcmdldC1jaGFpbi10aXApKQogICAgICAgIChpbm5lci1jb21taXQtYmxvY2sgYmxvY2sgY29tbWl0LWJsb2NrLWhlaWdodCB0YXJnZXQtYnVybi1ibG9jay1oZWlnaHQgd2l0aGRyYXdhbC1yb290KQogICAgKQopCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBORlQgQVNTRVQgVFJBTlNGRVJTCgo7OyBIZWxwZXIgZnVuY3Rpb24gdGhhdCB0cmFuc2ZlcnMgdGhlIHNwZWNpZmllZCBORlQgZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgdHJhbnNmZXIgaWQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChhcy1jb250cmFjdCAoY29udHJhY3QtY2FsbD8gbmZ0LW1pbnQtY29udHJhY3QgbWludC1mcm9tLXN1Ym5ldCBpZCBzZW5kZXIgcmVjaXBpZW50KSkpCiAgICAgICAgICAgIChtaW50LXJlc3VsdCAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICkKICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB0cmFuc2ZlciBzdWNjZWVkZWQKICAgICAgICAoYXNzZXJ0cyEgbWludC1yZXN1bHQgKGVyciBFUlJfTUlOVF9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikKICAgICAgICAoaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGNvbnRyYWN0LWNhbGw/IG5mdC1jb250cmFjdCBnZXQtb3duZXIgaWQpKQogICAgICAgICAgICAobmZ0LW93bmVyICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLW5mdCAoaXMtZXEgbmZ0LW93bmVyIChzb21lIENPTlRSQUNUX0FERFJFU1MpKSkKICAgICAgICAgICAgKG5vLW93bmVyIChpcy1lcSBuZnQtb3duZXIgbm9uZSkpCiAgICAgICAgKQoKICAgICAgICAoaWYgY29udHJhY3Qtb3ducy1uZnQKICAgICAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgIChpZiBuby1vd25lcgogICAgICAgICAgICAgICAgOzsgVHJ5IG1pbnRpbmcgdGhlIGFzc2V0IGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG93bmVyIG9mIHRoaXMgTkZUCiAgICAgICAgICAgICAgICAoaW5uZXItbWludC1uZnQtYXNzZXQgbmZ0LW1pbnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICAgICAgICAgICAgICA7OyBJbiB0aGlzIGNhc2UsIGEgcHJpbmNpcGFsIG90aGVyIHRoYW4gdGhpcyBjb250cmFjdCBvd25zIHRoaXMgTkZULCBzbyBtaW50aW5nIGlzIG5vdCBwb3NzaWJsZQogICAgICAgICAgICAgICAgKGVyciBFUlJfTUlOVF9GQUlMRUQpCiAgICAgICAgICAgICkKICAgICAgICApCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgYW4gTkZUIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQoKICAgICAgICA7OyBUcnkgdG8gdHJhbnNmZXIgdGhlIE5GVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1uZnQtYXNzZXQgbmZ0LWNvbnRyYWN0IGlkIHNlbmRlciBDT05UUkFDVF9BRERSRVNTKSkgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgOzsgRW1pdCBhIHByaW50IGV2ZW50IC0gdGhlIG5vZGUgY29uc3VtZXMgdGhpcwogICAgICAgIChwcmludCB7CiAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1uZnQiLAogICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IG5mdC1jb250cmFjdCksCiAgICAgICAgICAgIG5mdC1pZDogaWQsCiAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICBzdWJuZXQtY29udHJhY3QtaWQ6IHN1Ym5ldC1jb250cmFjdC1pZCwKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IEhlbHBlciBmdW5jdGlvbiBmb3IgYHdpdGhkcmF3LW5mdC1hc3NldGAKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChpbm5lci13aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChsMi1jb250cmFjdCBwcmluY2lwYWwpCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG5mdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAobGV0ICgoaGFzaGVzLWFyZS12YWxpZCAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMgd2l0aGRyYXdhbC1yb290IHdpdGhkcmF3YWwtbGVhZi1oYXNoIHNpYmxpbmctaGFzaGVzKSkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISBoYXNoZXMtYXJlLXZhbGlkKSAoZXJyIEVSUl9WQUxJREFUSU9OX0ZBSUxFRCkpCgogICAgICAgIDs7IGNoZWNrIHRoYXQgdGhlIHdpdGhkcmF3YWwgcmVxdWVzdCBkYXRhIG1hdGNoZXMgdGhlIHN1cHBsaWVkIGxlYWYgaGFzaAogICAgICAgIChhc3NlcnRzISAoaXMtZXEgd2l0aGRyYXdhbC1sZWFmLWhhc2gKICAgICAgICAgICAgICAgICAgICAgICAgIChsZWFmLWhhc2gtd2l0aGRyYXctbmZ0IGwyLWNvbnRyYWN0IGlkIHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBuZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgICAgIG1pbnQtY29udHJhY3QgKGFzLWNvbnRyYWN0IChpbm5lci10cmFuc2Zlci1vci1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgbWludC1jb250cmFjdCBpZCByZWNpcGllbnQpKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgcmVjaXBpZW50KSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKQogICAgICAgICkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIHdpdGhkcmF3IHRoZSBzcGVjaWZpZWQgTkZUIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1uZnQtYXNzZXQKICAgICAgICAobmZ0LWNvbnRyYWN0IDxuZnQtdHJhaXQ+KQogICAgICAgIChpZCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChuZnQtbWludC1jb250cmFjdCAob3B0aW9uYWwgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KSkKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIGFzc2V0IGJlbG9uZ3MgdG8gdGhlIGFsbG93ZWQtY29udHJhY3RzIG1hcAogICAgICAgICAgICAobDItY29udHJhY3QgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBuZnQtY29udHJhY3QpKSAoZXJyIEVSUl9ESVNBTExPV0VEX0FTU0VUKSkpCiAgICAgICAgKQogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgICAodHJ5ISAoaW5uZXItd2l0aGRyYXctbmZ0LWFzc2V0CiAgICAgICAgICAgICAgICBuZnQtY29udHJhY3QKICAgICAgICAgICAgICAgIGwyLWNvbnRyYWN0CiAgICAgICAgICAgICAgICBpZAogICAgICAgICAgICAgICAgcmVjaXBpZW50CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWlkCiAgICAgICAgICAgICAgICBoZWlnaHQKICAgICAgICAgICAgICAgIG5mdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLXJvb3QKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICBzaWJsaW5nLWhhc2hlcwogICAgICAgICAgICApKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgewogICAgICAgICAgICBldmVudDogIndpdGhkcmF3LW5mdCIsCiAgICAgICAgICAgIGwxLWNvbnRyYWN0LWlkOiAoYXMtY29udHJhY3QgbmZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgbmZ0LWlkOiBpZCwKICAgICAgICAgICAgcmVjaXBpZW50OiByZWNpcGllbnQKICAgICAgICB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7IExpa2UgYGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtbmZ0LWFzc2V0IGJ1dCB3aXRob3V0IGFsbG93aW5nIG9yIHJlcXVpcmluZyBhIG1pbnQgZnVuY3Rpb24uIEluIG9yZGVyIHRvIHdpdGhkcmF3LCB0aGUgdXNlciBtdXN0Cjs7IGhhdmUgdGhlIGFwcHJvcHJpYXRlIGJhbGFuY2UuCihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItdHJhbnNmZXItd2l0aG91dC1taW50LW5mdC1hc3NldAogICAgICAgIChuZnQtY29udHJhY3QgPG5mdC10cmFpdD4pCiAgICAgICAgKGlkIHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgKGNhbGwtcmVzdWx0IChjb250cmFjdC1jYWxsPyBuZnQtY29udHJhY3QgZ2V0LW93bmVyIGlkKSkKICAgICAgICAgICAgKG5mdC1vd25lciAodW53cmFwISBjYWxsLXJlc3VsdCAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICAoY29udHJhY3Qtb3ducy1uZnQgKGlzLWVxIG5mdC1vd25lciAoc29tZSBDT05UUkFDVF9BRERSRVNTKSkpCiAgICAgICAgKQoKICAgICAgICAoYXNzZXJ0cyEgY29udHJhY3Qtb3ducy1uZnQgKGVyciBFUlJfTkZUX05PVF9PV05FRF9CWV9DT05UUkFDVCkpCiAgICAgICAgKGlubmVyLXRyYW5zZmVyLW5mdC1hc3NldCBuZnQtY29udHJhY3QgaWQgQ09OVFJBQ1RfQUREUkVTUyByZWNpcGllbnQpCiAgICApCikKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgRk9SIEZVTkdJQkxFIFRPS0VOIEFTU0VUIFRSQU5TRkVSUwoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aGUgZnVuZ2libGUgdG9rZW4gZnJvbSB0aGUgZ2l2ZW4gc2VuZGVyIHRvIHRoZSBnaXZlbiByZWNpcGllbnQuCjs7IFJldHVybnMgcmVzcG9uc2U8Ym9vbCwgaW50PgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChzZW5kZXIgcHJpbmNpcGFsKQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgdHJhbnNmZXIgYW1vdW50IHNlbmRlciByZWNpcGllbnQgbWVtbykpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICApCiAgICAgICAgOzsgRklYTUU6IFNJUC0wMTAgZG9lc24ndCByZXF1aXJlIHRoYXQgdHJhbnNmZXIgcmV0dXJucyAob2sgdHJ1ZSkgb24gc3VjY2Vzcywgc28gaXMgdGhpcyBjaGVjayBuZWNlc3Nhcnk/CiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdHJhbnNmZXIgc3VjY2VlZGVkCiAgICAgICAgKGFzc2VydHMhIHRyYW5zZmVyLXJlc3VsdCAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1wcml2YXRlIChpbm5lci1taW50LWZ0LWFzc2V0CiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgPG1pbnQtZnJvbS1zdWJuZXQtdHJhaXQ+KQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAoc2VuZGVyIHByaW5jaXBhbCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY2FsbC1yZXN1bHQgKGFzLWNvbnRyYWN0IChjb250cmFjdC1jYWxsPyBmdC1taW50LWNvbnRyYWN0IG1pbnQtZnJvbS1zdWJuZXQgYW1vdW50IHNlbmRlciByZWNpcGllbnQpKSkKICAgICAgICAgICAgKG1pbnQtcmVzdWx0ICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISBtaW50LXJlc3VsdCAoZXJyIEVSUl9NSU5UX0ZBSUxFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgooZGVmaW5lLXByaXZhdGUgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoZnQtbWludC1jb250cmFjdCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoY29udHJhY3QtY2FsbD8gZnQtY29udHJhY3QgZ2V0LWJhbGFuY2UgQ09OVFJBQ1RfQUREUkVTUykpCiAgICAgICAgICAgIChjb250cmFjdC1mdC1iYWxhbmNlICh1bndyYXAhIGNhbGwtcmVzdWx0IChlcnIgRVJSX0NPTlRSQUNUX0NBTExfRkFJTEVEKSkpCiAgICAgICAgICAgIChjb250cmFjdC1vd25zLWVub3VnaCAoPj0gY29udHJhY3QtZnQtYmFsYW5jZSBhbW91bnQpKQogICAgICAgICAgICAoYW1vdW50LXRvLXRyYW5zZmVyIChpZiBjb250cmFjdC1vd25zLWVub3VnaCBhbW91bnQgY29udHJhY3QtZnQtYmFsYW5jZSkpCiAgICAgICAgICAgIChhbW91bnQtdG8tbWludCAoLSBhbW91bnQgYW1vdW50LXRvLXRyYW5zZmVyKSkKICAgICAgICApCgogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRvdGFsIGJhbGFuY2UgYmV0d2VlbiB0aGUgdHJhbnNmZXIgYW5kIG1pbnQgaXMgZXF1YWwgdG8gdGhlIG9yaWdpbmFsIGJhbGFuY2UKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIGFtb3VudCAoKyBhbW91bnQtdG8tdHJhbnNmZXIgYW1vdW50LXRvLW1pbnQpKSAoZXJyIEVSUl9JTl9DT01QVVRBVElPTikpCgogICAgICAgIChhbmQKICAgICAgICAgICAgKD4gYW1vdW50LXRvLXRyYW5zZmVyIHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50LXRvLXRyYW5zZmVyIENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICkKICAgICAgICAoYW5kCiAgICAgICAgICAgICg+IGFtb3VudC10by1taW50IHUwKQogICAgICAgICAgICAodHJ5ISAoaW5uZXItbWludC1mdC1hc3NldCBmdC1taW50LWNvbnRyYWN0IGFtb3VudC10by1taW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50KSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FsbHMgdGhpcyBmdW5jdGlvbiB0byBkZXBvc2l0IGEgZnVuZ2libGUgdG9rZW4gaW50byB0aGUgY29udHJhY3QuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKGRlcG9zaXQtZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHNlbmRlciBwcmluY2lwYWwpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICApCiAgICAobGV0ICgKICAgICAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgICAgIChzdWJuZXQtY29udHJhY3QtaWQgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKSkKICAgICAgICApCiAgICAgICAgOzsgVHJ5IHRvIHRyYW5zZmVyIHRoZSBGVCB0byB0aGlzIGNvbnRyYWN0CiAgICAgICAgKGFzc2VydHMhICh0cnkhIChpbm5lci10cmFuc2Zlci1mdC1hc3NldCBmdC1jb250cmFjdCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MgbWVtbykpIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkpCgogICAgICAgIChsZXQgKAogICAgICAgICAgICAgICAgKGZ0LW5hbWUgKHVud3JhcCEgKGNvbnRyYWN0LWNhbGw/IGZ0LWNvbnRyYWN0IGdldC1uYW1lKSAoZXJyIEVSUl9DT05UUkFDVF9DQUxMX0ZBSUxFRCkpKQogICAgICAgICAgICApCiAgICAgICAgICAgIDs7IEVtaXQgYSBwcmludCBldmVudCAtIHRoZSBub2RlIGNvbnN1bWVzIHRoaXMKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAiZGVwb3NpdC1mdCIsCiAgICAgICAgICAgICAgICBsMS1jb250cmFjdC1pZDogKGFzLWNvbnRyYWN0IGZ0LWNvbnRyYWN0KSwKICAgICAgICAgICAgICAgIGZ0LW5hbWU6IGZ0LW5hbWUsCiAgICAgICAgICAgICAgICBmdC1hbW91bnQ6IGFtb3VudCwKICAgICAgICAgICAgICAgIHNlbmRlcjogc2VuZGVyLAogICAgICAgICAgICAgICAgc3VibmV0LWNvbnRyYWN0LWlkOiBzdWJuZXQtY29udHJhY3QtaWQsCiAgICAgICAgICAgIH0pCiAgICAgICAgKQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBwZXJmb3JtcyB2YWxpZGl0eSBjaGVja3MgcmVsYXRlZCB0byB0aGUgd2l0aGRyYXdhbCBhbmQgcGVyZm9ybXMgdGhlIHdpdGhkcmF3YWwgYXMgd2VsbC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoaW5uZXItd2l0aGRyYXctZnQtYXNzZXQKICAgICAgICAoZnQtY29udHJhY3QgPGZ0LXRyYWl0PikKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKG1lbW8gKG9wdGlvbmFsIChidWZmIDM0KSkpCiAgICAgICAgKGZ0LW1pbnQtY29udHJhY3QgKG9wdGlvbmFsIDxtaW50LWZyb20tc3VibmV0LXRyYWl0PikpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgaGFzaGVzLWFyZS12YWxpZCkgKGVyciBFUlJfVkFMSURBVElPTl9GQUlMRUQpKQoKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LWZ0IChjb250cmFjdC1vZiBmdC1jb250cmFjdCkgYW1vdW50IHJlY2lwaWVudCB3aXRoZHJhd2FsLWlkIGhlaWdodCkpCiAgICAgICAgICAgICAgICAgIChlcnIgRVJSX1ZBTElEQVRJT05fTEVBRl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEKICAgICAgICAgICAgICAgIChtYXRjaCBmdC1taW50LWNvbnRyYWN0CiAgICAgICAgICAgICAgICAgICAgbWludC1jb250cmFjdCAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLW9yLW1pbnQtZnQtYXNzZXQgZnQtY29udHJhY3QgbWludC1jb250cmFjdCBhbW91bnQgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgICAgIChhcy1jb250cmFjdCAoaW5uZXItdHJhbnNmZXItZnQtYXNzZXQgZnQtY29udHJhY3QgYW1vdW50IENPTlRSQUNUX0FERFJFU1MgcmVjaXBpZW50IG1lbW8pKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCiAgICAgICAgICAgIChlcnIgRVJSX1RSQU5TRkVSX0ZBSUxFRCkKICAgICAgICApCgogICAgICAgIChhc3NlcnRzIQogICAgICAgICAgKGZpbmlzaC13aXRoZHJhdyB7IHdpdGhkcmF3YWwtbGVhZi1oYXNoOiB3aXRoZHJhd2FsLWxlYWYtaGFzaCwgd2l0aGRyYXdhbC1yb290LWhhc2g6IHdpdGhkcmF3YWwtcm9vdCB9KQogICAgICAgICAgKGVyciBFUlJfV0lUSERSQVdBTF9BTFJFQURZX1BST0NFU1NFRCkpCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgo7OyBBIHVzZXIgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB3aXRoZHJhdyBzb21lIGFtb3VudCBvZiBhIGZ1bmdpYmxlIHRva2VuIGFzc2V0IGZyb20gdGhlCjs7IGNvbnRyYWN0IGFuZCBzZW5kIGl0IHRvIGEgcmVjaXBpZW50Lgo7OyBJbiBvcmRlciBmb3IgdGhpcyB3aXRoZHJhd2FsIHRvIGdvIHRocm91Z2gsIHRoZSBnaXZlbiB3aXRoZHJhd2FsIG11c3QgaGF2ZSBiZWVuIGluY2x1ZGVkCjs7IGluIGEgd2l0aGRyYXdhbCBNZXJrbGUgdHJlZSBhIHN1Ym5ldCBtaW5lciBzdWJtaXR0ZWQuIFRoZSB1c2VyIG11c3QgcHJvdmlkZSB0aGUgbGVhZgo7OyBoYXNoIG9mIHRoZWlyIHdpdGhkcmF3YWwgYW5kIHRoZSByb290IGhhc2ggb2YgdGhlIHNwZWNpZmljIE1lcmtsZSB0cmVlIHRoZWlyIHdpdGhkcmF3YWwKOzsgaXMgaW5jbHVkZWQgaW4uIFRoZXkgbXVzdCBhbHNvIHByb3ZpZGUgYSBsaXN0IG9mIHNpYmxpbmcgaGFzaGVzLiBUaGUgd2l0aGRyYXcgZnVuY3Rpb24KOzsgdXNlcyB0aGUgcHJvdmlkZWQgaGFzaGVzIHRvIGVuc3VyZSB0aGUgcmVxdWVzdGVkIHdpdGhkcmF3YWwgaXMgdmFsaWQuCjs7IFRoZSBmdW5jdGlvbiBlbWl0cyBhIHByaW50IHdpdGggZGV0YWlscyBvZiB0aGlzIGV2ZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wdWJsaWMgKHdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgKGZ0LWNvbnRyYWN0IDxmdC10cmFpdD4pCiAgICAgICAgKGFtb3VudCB1aW50KQogICAgICAgIChyZWNpcGllbnQgcHJpbmNpcGFsKQogICAgICAgICh3aXRoZHJhd2FsLWlkIHVpbnQpCiAgICAgICAgKGhlaWdodCB1aW50KQogICAgICAgIChtZW1vIChvcHRpb25hbCAoYnVmZiAzNCkpKQogICAgICAgIChmdC1taW50LWNvbnRyYWN0IChvcHRpb25hbCA8bWludC1mcm9tLXN1Ym5ldC10cmFpdD4pKQogICAgICAgICh3aXRoZHJhd2FsLXJvb3QgKGJ1ZmYgMzIpKQogICAgICAgICh3aXRoZHJhd2FsLWxlYWYtaGFzaCAoYnVmZiAzMikpCiAgICAgICAgKHNpYmxpbmctaGFzaGVzIChsaXN0IDUwIHsKICAgICAgICAgICAgaGFzaDogKGJ1ZmYgMzIpLAogICAgICAgICAgICBpcy1sZWZ0LXNpZGU6IGJvb2wsCiAgICAgICAgfSkpCiAgICApCiAgICAoYmVnaW4KICAgICAgICA7OyBDaGVjayB0aGF0IHRoZSB3aXRoZHJhdyBhbW91bnQgaXMgcG9zaXRpdmUKICAgICAgICAoYXNzZXJ0cyEgKD4gYW1vdW50IHUwKSAoZXJyIEVSUl9BVFRFTVBUX1RPX1RSQU5TRkVSX1pFUk9fQU1PVU5UKSkKCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgYXNzZXQgYmVsb25ncyB0byB0aGUgYWxsb3dlZC1jb250cmFjdHMgbWFwCiAgICAgICAgKHVud3JhcCEgKG1hcC1nZXQ/IGFsbG93ZWQtY29udHJhY3RzIChjb250cmFjdC1vZiBmdC1jb250cmFjdCkpIChlcnIgRVJSX0RJU0FMTE9XRURfQVNTRVQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgICAgKHRyeSEgKGlubmVyLXdpdGhkcmF3LWZ0LWFzc2V0CiAgICAgICAgICAgICAgICBmdC1jb250cmFjdAogICAgICAgICAgICAgICAgYW1vdW50CiAgICAgICAgICAgICAgICByZWNpcGllbnQKICAgICAgICAgICAgICAgIHdpdGhkcmF3YWwtaWQKICAgICAgICAgICAgICAgIGhlaWdodAogICAgICAgICAgICAgICAgbWVtbwogICAgICAgICAgICAgICAgZnQtbWludC1jb250cmFjdAogICAgICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290CiAgICAgICAgICAgICAgICB3aXRoZHJhd2FsLWxlYWYtaGFzaAogICAgICAgICAgICAgICAgc2libGluZy1oYXNoZXMpKQogICAgICAgICAgICAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpCiAgICAgICAgKQoKICAgICAgICAobGV0ICgKICAgICAgICAgICAgICAgIChmdC1uYW1lICh1bndyYXAhIChjb250cmFjdC1jYWxsPyBmdC1jb250cmFjdCBnZXQtbmFtZSkgKGVyciBFUlJfQ09OVFJBQ1RfQ0FMTF9GQUlMRUQpKSkKICAgICAgICAgICAgKQogICAgICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAgICAgKHByaW50IHsKICAgICAgICAgICAgICAgIGV2ZW50OiAid2l0aGRyYXctZnQiLAogICAgICAgICAgICAgICAgbDEtY29udHJhY3QtaWQ6IChhcy1jb250cmFjdCBmdC1jb250cmFjdCksCiAgICAgICAgICAgICAgICBmdC1uYW1lOiBmdC1uYW1lLAogICAgICAgICAgICAgICAgZnQtYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgfSkKICAgICAgICApCgogICAgICAgIChvayB0cnVlKQogICAgKQopCgoKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEZPUiBTVFggVFJBTlNGRVJTCgoKOzsgSGVscGVyIGZ1bmN0aW9uIHRoYXQgdHJhbnNmZXJzIHRoZSBnaXZlbiBhbW91bnQgZnJvbSB0aGUgc3BlY2lmaWVkIGZ1bmdpYmxlIHRva2VuIGZyb20gdGhlIGdpdmVuIHNlbmRlciB0byB0aGUgZ2l2ZW4gcmVjaXBpZW50Lgo7OyBSZXR1cm5zIHJlc3BvbnNlPGJvb2wsIGludD4KKGRlZmluZS1wcml2YXRlIChpbm5lci10cmFuc2Zlci1zdHggKGFtb3VudCB1aW50KSAoc2VuZGVyIHByaW5jaXBhbCkgKHJlY2lwaWVudCBwcmluY2lwYWwpKQogICAgKGxldCAoCiAgICAgICAgICAgIChjYWxsLXJlc3VsdCAoc3R4LXRyYW5zZmVyPyBhbW91bnQgc2VuZGVyIHJlY2lwaWVudCkpCiAgICAgICAgICAgICh0cmFuc2Zlci1yZXN1bHQgKHVud3JhcCEgY2FsbC1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkpCiAgICAgICAgKQogICAgICAgIDs7IENoZWNrIHRoYXQgdGhlIHRyYW5zZmVyIHN1Y2NlZWRlZAogICAgICAgIChhc3NlcnRzISB0cmFuc2Zlci1yZXN1bHQgKGVyciBFUlJfVFJBTlNGRVJfRkFJTEVEKSkKCiAgICAgICAgKG9rIHRydWUpCiAgICApCikKCjs7IEEgdXNlciBjYWxscyB0aGlzIGZ1bmN0aW9uIHRvIGRlcG9zaXQgU1RYIGludG8gdGhlIGNvbnRyYWN0Lgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljIChkZXBvc2l0LXN0eCAoYW1vdW50IHVpbnQpIChzZW5kZXIgcHJpbmNpcGFsKSkKICAgIChiZWdpbgogICAgICAgIDs7IFRyeSB0byB0cmFuc2ZlciB0aGUgU1RYIHRvIHRoaXMgY29udHJhY3QKICAgICAgICAoYXNzZXJ0cyEgKHRyeSEgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgc2VuZGVyIENPTlRSQUNUX0FERFJFU1MpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQgLSB0aGUgbm9kZSBjb25zdW1lcyB0aGlzCiAgICAgICAgKHByaW50IHsgZXZlbnQ6ICJkZXBvc2l0LXN0eCIsIHNlbmRlcjogc2VuZGVyLCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICApCiAgICAoc2hhNTEyLzI1NiAoY29uY2F0IDB4MDAgKHVud3JhcC1wYW5pYyAodG8tY29uc2Vuc3VzLWJ1ZmY/CiAgICAgICAgewogICAgICAgICAgICB0eXBlOiAic3R4IiwKICAgICAgICAgICAgYW1vdW50OiBhbW91bnQsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGxlYWYtaGFzaC13aXRoZHJhdy1uZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChuZnQtaWQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJuZnQiLAogICAgICAgICAgICBuZnQtaWQ6IG5mdC1pZCwKICAgICAgICAgICAgYXNzZXQtY29udHJhY3Q6IGFzc2V0LWNvbnRyYWN0LAogICAgICAgICAgICByZWNpcGllbnQ6IHJlY2lwaWVudCwKICAgICAgICAgICAgd2l0aGRyYXdhbC1pZDogd2l0aGRyYXdhbC1pZCwKICAgICAgICAgICAgaGVpZ2h0OiBoZWlnaHQKICAgICAgICB9KSkpCiAgICApCikKCihkZWZpbmUtcmVhZC1vbmx5IChsZWFmLWhhc2gtd2l0aGRyYXctZnQKICAgICAgICAoYXNzZXQtY29udHJhY3QgcHJpbmNpcGFsKQogICAgICAgIChhbW91bnQgdWludCkKICAgICAgICAocmVjaXBpZW50IHByaW5jaXBhbCkKICAgICAgICAod2l0aGRyYXdhbC1pZCB1aW50KQogICAgICAgIChoZWlnaHQgdWludCkKICAgICkKICAgIChzaGE1MTIvMjU2IChjb25jYXQgMHgwMCAodW53cmFwLXBhbmljICh0by1jb25zZW5zdXMtYnVmZj8KICAgICAgICB7CiAgICAgICAgICAgIHR5cGU6ICJmdCIsCiAgICAgICAgICAgIGFtb3VudDogYW1vdW50LAogICAgICAgICAgICBhc3NldC1jb250cmFjdDogYXNzZXQtY29udHJhY3QsCiAgICAgICAgICAgIHJlY2lwaWVudDogcmVjaXBpZW50LAogICAgICAgICAgICB3aXRoZHJhd2FsLWlkOiB3aXRoZHJhd2FsLWlkLAogICAgICAgICAgICBoZWlnaHQ6IGhlaWdodAogICAgICAgIH0pKSkKICAgICkKKQoKOzsgQSB1c2VyIGNhbGxzIHRoaXMgZnVuY3Rpb24gdG8gd2l0aGRyYXcgU1RYIGZyb20gdGhpcyBjb250cmFjdC4KOzsgSW4gb3JkZXIgZm9yIHRoaXMgd2l0aGRyYXdhbCB0byBnbyB0aHJvdWdoLCB0aGUgZ2l2ZW4gd2l0aGRyYXdhbCBtdXN0IGhhdmUgYmVlbiBpbmNsdWRlZAo7OyBpbiBhIHdpdGhkcmF3YWwgTWVya2xlIHRyZWUgYSBzdWJuZXQgbWluZXIgc3VibWl0dGVkLiBUaGUgdXNlciBtdXN0IHByb3ZpZGUgdGhlIGxlYWYKOzsgaGFzaCBvZiB0aGVpciB3aXRoZHJhd2FsIGFuZCB0aGUgcm9vdCBoYXNoIG9mIHRoZSBzcGVjaWZpYyBNZXJrbGUgdHJlZSB0aGVpciB3aXRoZHJhd2FsCjs7IGlzIGluY2x1ZGVkIGluLiBUaGV5IG11c3QgYWxzbyBwcm92aWRlIGEgbGlzdCBvZiBzaWJsaW5nIGhhc2hlcy4gVGhlIHdpdGhkcmF3IGZ1bmN0aW9uCjs7IHVzZXMgdGhlIHByb3ZpZGVkIGhhc2hlcyB0byBlbnN1cmUgdGhlIHJlcXVlc3RlZCB3aXRoZHJhd2FsIGlzIHZhbGlkLgo7OyBUaGUgZnVuY3Rpb24gZW1pdHMgYSBwcmludCB3aXRoIGRldGFpbHMgb2YgdGhpcyBldmVudC4KOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHVibGljICh3aXRoZHJhdy1zdHgKICAgICAgICAoYW1vdW50IHVpbnQpCiAgICAgICAgKHJlY2lwaWVudCBwcmluY2lwYWwpCiAgICAgICAgKHdpdGhkcmF3YWwtaWQgdWludCkKICAgICAgICAoaGVpZ2h0IHVpbnQpCiAgICAgICAgKHdpdGhkcmF3YWwtcm9vdCAoYnVmZiAzMikpCiAgICAgICAgKHdpdGhkcmF3YWwtbGVhZi1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoZXMgKGxpc3QgNTAgewogICAgICAgICAgICBoYXNoOiAoYnVmZiAzMiksCiAgICAgICAgICAgIGlzLWxlZnQtc2lkZTogYm9vbCwKICAgICAgICB9KSkKICAgICkKICAgIChsZXQgKChoYXNoZXMtYXJlLXZhbGlkIChjaGVjay13aXRoZHJhd2FsLWhhc2hlcyB3aXRoZHJhd2FsLXJvb3Qgd2l0aGRyYXdhbC1sZWFmLWhhc2ggc2libGluZy1oYXNoZXMpKSkKCiAgICAgICAgKGFzc2VydHMhICh0cnkhIGhhc2hlcy1hcmUtdmFsaWQpIChlcnIgRVJSX1ZBTElEQVRJT05fRkFJTEVEKSkKICAgICAgICA7OyBjaGVjayB0aGF0IHRoZSB3aXRoZHJhd2FsIHJlcXVlc3QgZGF0YSBtYXRjaGVzIHRoZSBzdXBwbGllZCBsZWFmIGhhc2gKICAgICAgICAoYXNzZXJ0cyEgKGlzLWVxIHdpdGhkcmF3YWwtbGVhZi1oYXNoCiAgICAgICAgICAgICAgICAgICAgICAgICAobGVhZi1oYXNoLXdpdGhkcmF3LXN0eCBhbW91bnQgcmVjaXBpZW50IHdpdGhkcmF3YWwtaWQgaGVpZ2h0KSkKICAgICAgICAgICAgICAgICAgKGVyciBFUlJfVkFMSURBVElPTl9MRUFGX0ZBSUxFRCkpCgogICAgICAgIChhc3NlcnRzISAodHJ5ISAoYXMtY29udHJhY3QgKGlubmVyLXRyYW5zZmVyLXN0eCBhbW91bnQgdHgtc2VuZGVyIHJlY2lwaWVudCkpKSAoZXJyIEVSUl9UUkFOU0ZFUl9GQUlMRUQpKQoKICAgICAgICAoYXNzZXJ0cyEKICAgICAgICAgIChmaW5pc2gtd2l0aGRyYXcgeyB3aXRoZHJhd2FsLWxlYWYtaGFzaDogd2l0aGRyYXdhbC1sZWFmLWhhc2gsIHdpdGhkcmF3YWwtcm9vdC1oYXNoOiB3aXRoZHJhd2FsLXJvb3QgfSkKICAgICAgICAgIChlcnIgRVJSX1dJVEhEUkFXQUxfQUxSRUFEWV9QUk9DRVNTRUQpKQoKICAgICAgICA7OyBFbWl0IGEgcHJpbnQgZXZlbnQKICAgICAgICAocHJpbnQgeyBldmVudDogIndpdGhkcmF3LXN0eCIsIHJlY2lwaWVudDogcmVjaXBpZW50LCBhbW91bnQ6IGFtb3VudCB9KQoKICAgICAgICAob2sgdHJ1ZSkKICAgICkKKQoKCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBHRU5FUkFMIFdJVEhEUkFXQUwgRlVOQ1RJT05TCgo7OyBUaGlzIGZ1bmN0aW9uIGNvbmNhdHMgdGhlIHR3byBnaXZlbiBoYXNoZXMgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuIEl0IGFsc28gcHJlcGVuZHMgdGhlIGJ1ZmYgYDB4MDFgLCB3aGljaCBpcwo7OyBhIHRhZyBkZW5vdGluZyBhIG5vZGUgKHZlcnN1cyBhIGxlYWYpLgo7OyBSZXR1cm5zIGEgYnVmZgooZGVmaW5lLXByaXZhdGUgKGNyZWF0ZS1ub2RlLWhhc2gKICAgICAgICAoY3Vyci1oYXNoIChidWZmIDMyKSkKICAgICAgICAoc2libGluZy1oYXNoIChidWZmIDMyKSkKICAgICAgICAoaXMtc2libGluZy1sZWZ0LXNpZGUgYm9vbCkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoY29uY2F0dGVkLWhhc2ggKGlmIGlzLXNpYmxpbmctbGVmdC1zaWRlCiAgICAgICAgICAgICAgICAgICAgKGNvbmNhdCBzaWJsaW5nLWhhc2ggY3Vyci1oYXNoKQogICAgICAgICAgICAgICAgICAgIChjb25jYXQgY3Vyci1oYXNoIHNpYmxpbmctaGFzaCkKICAgICAgICAgICAgICAgICkpCiAgICAgICAgICApCgogICAgICAgICAgKGNvbmNhdCAweDAxIGNvbmNhdHRlZC1oYXNoKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIGhhc2hlcyB0aGUgY3VyciBoYXNoIHdpdGggaXRzIHNpYmxpbmcgaGFzaC4KOzsgUmV0dXJucyAoYnVmZiAzMikKKGRlZmluZS1wcml2YXRlIChoYXNoLWhlbHAKICAgICAgICAoc2libGluZyB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pCiAgICAgICAgKGN1cnItbm9kZS1oYXNoIChidWZmIDMyKSkKICAgICkKICAgIChsZXQgKAogICAgICAgICAgICAoc2libGluZy1oYXNoIChnZXQgaGFzaCBzaWJsaW5nKSkKICAgICAgICAgICAgKGlzLXNpYmxpbmctbGVmdC1zaWRlIChnZXQgaXMtbGVmdC1zaWRlIHNpYmxpbmcpKQogICAgICAgICAgICAobmV3LWJ1ZmYgKGNyZWF0ZS1ub2RlLWhhc2ggY3Vyci1ub2RlLWhhc2ggc2libGluZy1oYXNoIGlzLXNpYmxpbmctbGVmdC1zaWRlKSkKICAgICAgICApCiAgICAgICAoc2hhNTEyLzI1NiBuZXctYnVmZikKICAgICkKKQoKOzsgVGhpcyBmdW5jdGlvbiBjaGVja3M6Cjs7ICAtIFRoYXQgdGhlIHByb3ZpZGVkIHdpdGhkcmF3YWwgcm9vdCBtYXRjaGVzIGEgcHJldmlvdXNseSBzdWJtaXR0ZWQgb25lIChwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uIGBjb21taXQtYmxvY2tgKQo7OyAgLSBUaGF0IHRoZSBjb21wdXRlZCB3aXRoZHJhd2FsIHJvb3QgbWF0Y2hlcyBhIHByZXZpb3VzIHZhbGlkIHdpdGhkcmF3YWwgcm9vdAo7OyAgLSBUaGF0IHRoZSBnaXZlbiB3aXRoZHJhd2FsIGxlYWYgaGFzaCBoYXMgbm90IGJlZW4gcHJldmlvdXNseSBwcm9jZXNzZWQKOzsgUmV0dXJucyByZXNwb25zZTxib29sLCBpbnQ+CihkZWZpbmUtcHJpdmF0ZSAoY2hlY2std2l0aGRyYXdhbC1oYXNoZXMKICAgICAgICAod2l0aGRyYXdhbC1yb290IChidWZmIDMyKSkKICAgICAgICAod2l0aGRyYXdhbC1sZWFmLWhhc2ggKGJ1ZmYgMzIpKQogICAgICAgIChzaWJsaW5nLWhhc2hlcyAobGlzdCA1MCB7CiAgICAgICAgICAgIGhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgaXMtbGVmdC1zaWRlOiBib29sLAogICAgICAgIH0pKQogICAgKQogICAgKGJlZ2luCiAgICAgICAgOzsgQ2hlY2sgdGhhdCB0aGUgdXNlciBzdWJtaXR0ZWQgYSB2YWxpZCB3aXRoZHJhd2FsIHJvb3QKICAgICAgICAoYXNzZXJ0cyEgKGlzLXNvbWUgKG1hcC1nZXQ/IHdpdGhkcmF3YWwtcm9vdHMtbWFwIHdpdGhkcmF3YWwtcm9vdCkpIChlcnIgRVJSX0lOVkFMSURfTUVSS0xFX1JPT1QpKQoKICAgICAgICA7OyBDaGVjayB0aGF0IHRoaXMgd2l0aGRyYXdhbCBsZWFmIGhhcyBub3QgYmVlbiBwcm9jZXNzZWQgYmVmb3JlCiAgICAgICAgKGFzc2VydHMhCiAgICAgICAgICAgIChpcy1ub25lCiAgICAgICAgICAgICAobWFwLWdldD8gcHJvY2Vzc2VkLXdpdGhkcmF3YWwtbGVhdmVzLW1hcAogICAgICAgICAgICAgICAgICAgICAgIHsgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IHdpdGhkcmF3YWwtbGVhZi1oYXNoLCB3aXRoZHJhd2FsLXJvb3QtaGFzaDogd2l0aGRyYXdhbC1yb290IH0pKQogICAgICAgICAgICAoZXJyIEVSUl9XSVRIRFJBV0FMX0FMUkVBRFlfUFJPQ0VTU0VEKSkKCiAgICAgICAgKGxldCAoKGNhbGN1bGF0ZWQtd2l0aGRyYXdhbC1yb290IChmb2xkIGhhc2gtaGVscCBzaWJsaW5nLWhhc2hlcyB3aXRoZHJhd2FsLWxlYWYtaGFzaCkpCiAgICAgICAgICAgICAgKHJvb3RzLW1hdGNoIChpcy1lcSBjYWxjdWxhdGVkLXdpdGhkcmF3YWwtcm9vdCB3aXRoZHJhd2FsLXJvb3QpKSkKICAgICAgICAgICAgIChpZiByb290cy1tYXRjaAogICAgICAgICAgICAgICAgKG9rIHRydWUpCiAgICAgICAgICAgICAgICAoZXJyIEVSUl9NRVJLTEVfUk9PVF9ET0VTX05PVF9NQVRDSCkpCiAgICAgICAgKQogICAgKQopCgo7OyBUaGlzIGZ1bmN0aW9uIHNob3VsZCBiZSBjYWxsZWQgYWZ0ZXIgdGhlIGFzc2V0IGluIHF1ZXN0aW9uIGhhcyBiZWVuIHRyYW5zZmVycmVkLgo7OyBJdCBhZGRzIHRoZSB3aXRoZHJhd2FsIGxlYWYgaGFzaCB0byBhIG1hcCBvZiBwcm9jZXNzZWQgbGVhdmVzLiBUaGlzIGVuc3VyZXMgdGhhdAo7OyB0aGlzIHdpdGhkcmF3YWwgbGVhZiBjYW4ndCBiZSB1c2VkIGFnYWluIHRvIHdpdGhkcmF3IGFkZGl0aW9uYWwgZnVuZHMuCjs7IFJldHVybnMgYm9vbAooZGVmaW5lLXByaXZhdGUgKGZpbmlzaC13aXRoZHJhdwogICAgICAgICh3aXRoZHJhdy1pbmZvIHsKICAgICAgICAgICAgd2l0aGRyYXdhbC1sZWFmLWhhc2g6IChidWZmIDMyKSwKICAgICAgICAgICAgd2l0aGRyYXdhbC1yb290LWhhc2g6IChidWZmIDMyKQogICAgICAgIH0pCiAgICApCiAgICAobWFwLWluc2VydCBwcm9jZXNzZWQtd2l0aGRyYXdhbC1sZWF2ZXMtbWFwIHdpdGhkcmF3LWluZm8gdHJ1ZSkKKQo=", + "clarity_version": 2, + "cost": 290960, + "location": { + "path": "./.cache/requirements/ST13F481SBR0R7Z6NMMH8YV2FJJYXA5JPA0AD3HP9.subnet-v1-2.clar" } - ], - "epoch": null - } - ] - }, - "contracts": { - "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px": [ - "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", - { - "path": "contracts/px.clar" - } - ] - } + }, + { + "transaction_type": "ContractPublish", + "contract_name": "px", + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "location": { + "path": "contracts/px.clar" + }, + "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK", + "clarity_version": 2, + "cost": 18060, + "anchor_block_only": true + } + ], + "epoch": "2.1" + }, + { + "id": 3, + "transactions": [ + { + "transaction_type": "ContractCall", + "contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px", + "expected_sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + "method": "set-value-at", + "parameters": ["u0", "0xfffffa"], + "cost": 2240, + "anchor_block_only": false + }, + { + "transaction_type": "StxTransfer", + "expected_sender": "ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB", + "recipient": "ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0", + "mstx_amount": 200, + "memo": "0x00000000000000000000000000000000000000000000000000000000000000000000", + "cost": 2240, + "anchor_block_only": true + } + ], + "epoch": "2.1" + } + ], + "contracts": [ + { + "contract_id": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.px", + "path": "/Users/micaiahreid/work/stx-px/contracts/px.clar", + "source": "Cjs7IHRpdGxlOiBweAo7OyB2ZXJzaW9uOgo7OyBzdW1tYXJ5Ogo7OyBkZXNjcmlwdGlvbjogQWxsb3dzIHVzZXJzIHRvIHBheSB0byB1cGRhdGUgZGF0YSBpbiBhIG1hdHJpeC4gCjs7ICBFYWNoIG1hdHJpeCB2YWx1ZSBtdXN0IGJlIGEgaGV4YWRlY2ltYWwgdmFsdWUgZnJvbSAweDAwMDAwMCB0byAweGZmZmZmZiwgcmVwcmVzZW50aW5nIGEgY29sb3IgdG8gYmUgZGlzcGxheWVkIG9uIGEgZ3JpZCBpbiBhIHdlYiBwYWdlLiAKOzsgIEVhY2ggbWF0cml4IGtleSBjb3JyZXNwb25kcyB0byB0aGUgbG9jYXRpb24gb2YgdGhlIGdyaWQsIHdoaWNoIGlzIDEwMHgxMDAgY2VsbHMuCgo7OyB0cmFpdHMKOzsKCjs7IHRva2VuIGRlZmluaXRpb25zCjs7IAoKOzsgY29uc3RhbnRzCjs7CihkZWZpbmUtY29uc3RhbnQgTUFYX0xPQyB1MTAwKQooZGVmaW5lLWNvbnN0YW50IE1BWF9WQUwgMHhmZmZmZmYpCihkZWZpbmUtY29uc3RhbnQgTUlOX1ZBTCAweDAwMDAwMCkKKGRlZmluZS1jb25zdGFudCBBTExfTE9DUyAobGlzdCB1MCB1MSB1MiB1MyB1NCB1NSB1NiB1NyB1OCB1OSB1MTAgdTExIHUxMiB1MTMgdTE0IHUxNSB1MTYgdTE3IHUxOCB1MTkgdTIwIHUyMSB1MjIgdTIzIHUyNCB1MjUgdTI2IHUyNyB1MjggdTI5IHUzMCB1MzEgdTMyIHUzMyB1MzQgdTM1IHUzNiB1MzcgdTM4IHUzOSB1NDAgdTQxIHU0MiB1NDMgdTQ0IHU0NSB1NDYgdTQ3IHU0OCB1NDkgdTUwIHU1MSB1NTIgdTUzIHU1NCB1NTUgdTU2IHU1NyB1NTggdTU5IHU2MCB1NjEgdTYyIHU2MyB1NjQgdTY1IHU2NiB1NjcgdTY4IHU2OSB1NzAgdTcxIHU3MiB1NzMgdTc0IHU3NSB1NzYgdTc3IHU3OCB1NzkgdTgwIHU4MSB1ODIgdTgzIHU4NCB1ODUgdTg2IHU4NyB1ODggdTg5IHU5MCB1OTEgdTkyIHU5MyB1OTQgdTk1IHU5NiB1OTcgdTk4IHU5OSkpCjs7IGRhdGEgdmFycwo7OwoKOzsgZGF0YSBtYXBzCjs7CihkZWZpbmUtbWFwIHBpeGVscyB1aW50IChidWZmIDMpKQoKOzsgcHVibGljIGZ1bmN0aW9ucwo7OwooZGVmaW5lLXB1YmxpYyAoc2V0LXZhbHVlLWF0IChsb2MgdWludCkgKHZhbHVlIChidWZmIDMpKSkgCiAgICAoYmVnaW4gCiAgICAgICAgKGlmICg+PSBsb2MgTUFYX0xPQykKICAgICAgICAgICAgKGVyciAiTG9jYXRpb24gb3V0IG9mIGJvdW5kcy4iKQogICAgICAgICAgICAoaWYgKD4gdmFsdWUgTUFYX1ZBTCkKICAgICAgICAgICAgICAgIChlcnIgIlZhbHVlIG11c3QgYmUgbGVzcyB0aGFuIDB4ZmZmZmZmLiIpCiAgICAgICAgICAgICAgICAoaWYgKDwgdmFsdWUgTUlOX1ZBTCkKICAgICAgICAgICAgICAgICAgICAoZXJyICJWYWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAweDAwMDAwMC4iKQogICAgICAgICAgICAgICAgICAgIChvayAobWFwLXNldCBwaXhlbHMgbG9jIHZhbHVlKSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQogICAgICAgICkKICAgICkKKQo7OyByZWFkIG9ubHkgZnVuY3Rpb25zCjs7CgooZGVmaW5lLXJlYWQtb25seSAoZ2V0LXZhbHVlLWF0IChsb2MgdWludCkpCiAgICAoaWYgKD49IGxvYyBNQVhfTE9DKQogICAgICAgIChlcnIgIk91dCBvZiBib3VuZHMuIikKICAgICAgICAob2sgKGRlZmF1bHQtdG8gMHhmZmZmZmYgKG1hcC1nZXQ/IHBpeGVscyBsb2MpKSkKICAgICkKKQoKKGRlZmluZS1yZWFkLW9ubHkgKGdldC1hbGwpIAogICAgKG1hcCBnZXQtdmFsdWUtYXQgQUxMX0xPQ1MpCikKCihkZWZpbmUtcmVhZC1vbmx5IChnZW5lc2lzLXRpbWUgKGhlaWdodCB1aW50KSkKICAgIChnZXQtYmxvY2staW5mbz8gdGltZSBoZWlnaHQpCikKOzsgcHJpdmF0ZSBmdW5jdGlvbnMKOzsK" + } + ] }, "network_manifest": { "network": { @@ -161,8 +134,8 @@ "deployment_fee_rate": 10, "sats_per_bytes": 10 }, - "accounts": { - "deployer": { + "accounts": [ + { "label": "deployer", "mnemonic": "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw", "derivation": "m/44'/5757'/0'/0/0", @@ -171,7 +144,7 @@ "btc_address": "mqVnk6NPRdhntvfm4hh9vvjiRkFDUuSYsH", "is_mainnet": false }, - "faucet": { + { "label": "faucet", "mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", "derivation": "m/44'/5757'/0'/0/0", @@ -180,7 +153,7 @@ "btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", "is_mainnet": false }, - "wallet_1": { + { "label": "wallet_1", "mnemonic": "crazy vibrant runway diagram beach language above aerobic maze coral this gas mirror output vehicle cover usage ecology unfold room feel file rocket expire", "derivation": "m/44'/5757'/0'/0/0", @@ -189,7 +162,7 @@ "btc_address": "mha4u7F3e93P9Xy1WQgVvGtYtynnJtT22x", "is_mainnet": false }, - "wallet_2": { + { "label": "wallet_2", "mnemonic": "hold excess usual excess ring elephant install account glad dry fragile donkey gaze humble truck breeze nation gasp vacuum limb head keep delay hospital", "derivation": "m/44'/5757'/0'/0/0", @@ -198,7 +171,7 @@ "btc_address": "muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG", "is_mainnet": false }, - "wallet_3": { + { "label": "wallet_3", "mnemonic": "cycle puppy glare enroll cost improve round trend wrist mushroom scorpion tower claim oppose clever elephant dinosaur eight problem before frozen dune wagon high", "derivation": "m/44'/5757'/0'/0/0", @@ -207,7 +180,7 @@ "btc_address": "mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7", "is_mainnet": false }, - "wallet_4": { + { "label": "wallet_4", "mnemonic": "board list obtain sugar hour worth raven scout denial thunder horse logic fury scorpion fold genuine phrase wealth news aim below celery when cabin", "derivation": "m/44'/5757'/0'/0/0", @@ -216,7 +189,7 @@ "btc_address": "mg1C76bNTutiCDV3t9nWhZs3Dc8LzUufj8", "is_mainnet": false }, - "wallet_5": { + { "label": "wallet_5", "mnemonic": "hurry aunt blame peanut heavy update captain human rice crime juice adult scale device promote vast project quiz unit note reform update climb purchase", "derivation": "m/44'/5757'/0'/0/0", @@ -225,7 +198,7 @@ "btc_address": "mweN5WVqadScHdA81aATSdcVr4B6dNokqx", "is_mainnet": false }, - "wallet_6": { + { "label": "wallet_6", "mnemonic": "area desk dutch sign gold cricket dawn toward giggle vibrant indoor bench warfare wagon number tiny universe sand talk dilemma pottery bone trap buddy", "derivation": "m/44'/5757'/0'/0/0", @@ -234,7 +207,7 @@ "btc_address": "mzxXgV6e4BZSsz8zVHm3TmqbECt7mbuErt", "is_mainnet": false }, - "wallet_7": { + { "label": "wallet_7", "mnemonic": "prevent gallery kind limb income control noise together echo rival record wedding sense uncover school version force bleak nuclear include danger skirt enact arrow", "derivation": "m/44'/5757'/0'/0/0", @@ -243,7 +216,7 @@ "btc_address": "n37mwmru2oaVosgfuvzBwgV2ysCQRrLko7", "is_mainnet": false }, - "wallet_8": { + { "label": "wallet_8", "mnemonic": "female adjust gallery certain visit token during great side clown fitness like hurt clip knife warm bench start reunion globe detail dream depend fortune", "derivation": "m/44'/5757'/0'/0/0", @@ -252,8 +225,8 @@ "btc_address": "n2v875jbJ4RjBnTjgbfikDfnwsDV5iUByw", "is_mainnet": false } - }, - "devnet": { + ], + "devnet_settings": { "name": "devnet", "network_id": null, "orchestrator_ingestion_port": 20445, @@ -288,7 +261,7 @@ "faucet_btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", "faucet_mnemonic": "shadow private easily thought say logic fault paddle word top book during ignore notable orange flight clock image wealth health outside kitten belt reform", "faucet_derivation_path": "m/44'/5757'/0'/0/0", - "working_dir": "/Users/micaiahreid/work/stx-px/tmp", + "working_dir": "/tmp", "postgres_port": 5432, "postgres_username": "postgres", "postgres_password": "postgres", @@ -324,10 +297,10 @@ "stacks_explorer_image_url": "hirosystems/explorer:latest", "postgres_image_url": "postgres:14", "bitcoin_explorer_image_url": "quay.io/hirosystems/bitcoin-explorer:devnet", - "disable_bitcoin_explorer": false, - "disable_stacks_explorer": false, + "disable_bitcoin_explorer": true, + "disable_stacks_explorer": true, "disable_stacks_api": false, - "bind_containers_volumes": false, + "bind_containers_volumes": true, "enable_subnet_node": false, "subnet_node_image_url": "hirosystems/stacks-subnets:0.8.1", "subnet_leader_stx_address": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", @@ -363,37 +336,34 @@ "project_manifest": { "project": { "name": "px", - "description": "", - "authors": [], - "telemetry": true, - "cache_dir": "./.cache", + "description": "my description", + "authors": ["test1", "test2"], + "telemetry": false, + "cache_dir": ".cache", "requirements": [] }, "contracts": { - "px": { - "path": "contracts/px.clar", + "my-contract": { + "path": "contracts/my-contract.clar", "clarity_version": 2, - "epoch": 2.1 + "epoch": 2.4 }, - "test": { - "path": "contracts/test.clar", + "px": { + "path": "contracts/px.clar", "clarity_version": 2, "epoch": 2.1 } }, "repl": { "analysis": { - "passes": ["check_checker"], + "passes": [], "check_checker": { - "strict": true, + "strict": false, "trusted_sender": false, "trusted_caller": false, "callee_filter": false } } - }, - "location": { - "path": "Clarinet.toml" } } } diff --git a/src/tests/fixtures/project-manifest.yaml b/src/tests/fixtures/project-manifest.yaml index 15a648b..2f08b5c 100644 --- a/src/tests/fixtures/project-manifest.yaml +++ b/src/tests/fixtures/project-manifest.yaml @@ -1,5 +1,5 @@ --- -metadata: +project: name: px description: my description authors: diff --git a/src/tests/fixtures/stacks-devnet-config.json b/src/tests/fixtures/stacks-devnet-config.json index 860d6f9..fd57f0c 100644 --- a/src/tests/fixtures/stacks-devnet-config.json +++ b/src/tests/fixtures/stacks-devnet-config.json @@ -336,7 +336,7 @@ } }, "project_manifest": { - "metadata": { + "project": { "name": "px", "description": "my description", "authors": ["test1", "test2"], From ef1ebde27f584a6052b521f58bec191aedfe8cfd Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Thu, 21 Sep 2023 14:48:29 -0400 Subject: [PATCH 08/35] chore: remove image pull secret for public registry (#57) --- .github/workflows/ci.yaml | 2 +- README.md | 7 +------ templates/stacks-devnet-api.template.yaml | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8f6520a..6c13da4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -118,7 +118,7 @@ jobs: run: | cargo install cargo-tarpaulin cargo --version - cargo tarpaulin --out Lcov + cargo tarpaulin --out lcov - name: Upload to codecov.io uses: codecov/codecov-action@v2 diff --git a/README.md b/README.md index cc2b61d..e04d256 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,8 @@ The `Config.toml` at the root directory of the project can be used to control so - `allowed_methods` - this setting is an array of strings that sets what HTTP methods can be made to this server. ## Deploying the Stable Version -First, you'll need to use your docker credentials to be able to pull the docker image. To create the needed secret, in your terminal run: -``` -kubectl create secret --namespace devnet docker-registry stacks-devnet-api-secret --docker-server=https://index.docker.io/v1/ --docker-username= --docker-email= --docker-password= -``` -and enter in the details for a docker user that has access to the `hirosystems/stacks-devnet-api` image. -Then, in your terminal, run +In your terminal, run ``` ./scripts/deploy-api.sh ``` diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index 5aaf233..ed8727f 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -40,8 +40,6 @@ metadata: namespace: devnet spec: serviceAccountName: stacks-devnet-api-service-account - imagePullSecrets: - - name: stacks-devnet-api-secret containers: - command: ["stacks-devnet-api"] name: stacks-devnet-api-container From 2ea50eaef6097c582ee103665d8c49e916b45bcd Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Thu, 21 Sep 2023 14:48:54 -0400 Subject: [PATCH 09/35] fix: reduce permissions for cluster role (#54) --- templates/ci/stacks-devnet-api.template.yaml | 6 ++++-- templates/stacks-devnet-api.template.yaml | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/templates/ci/stacks-devnet-api.template.yaml b/templates/ci/stacks-devnet-api.template.yaml index 711243f..1de5aab 100644 --- a/templates/ci/stacks-devnet-api.template.yaml +++ b/templates/ci/stacks-devnet-api.template.yaml @@ -11,9 +11,11 @@ metadata: name: stacks-devnet-api-service-account rules: - apiGroups: [""] - # TODO: production version should not be able to create/delete namespaces (only get) - resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims", "namespaces"] + resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] verbs: ["get", "delete", "create"] + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index ed8727f..62299f2 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -11,9 +11,12 @@ metadata: name: stacks-devnet-api-service-account rules: - apiGroups: [""] - # TODO: production version should not be able to create/delete namespaces (only get) - resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims", "namespaces"] + resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] verbs: ["get", "delete", "create"] + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["get"] + --- apiVersion: rbac.authorization.k8s.io/v1 From 215c70563c5d26d8b264c84d7f195b840042e33d Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Fri, 22 Sep 2023 09:49:50 -0400 Subject: [PATCH 10/35] chore: change some `warn` logging to `info` (#60) --- src/lib.rs | 6 +++--- src/main.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2384a62..40d94c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,7 +252,7 @@ impl StacksDevnetApiK8sManager { pub async fn check_namespace_exists(&self, namespace_str: &str) -> Result { self.ctx.try_log(|logger| { - slog::warn!( + slog::info!( logger, "checking if namespace NAMESPACE: {}", &namespace_str @@ -296,7 +296,7 @@ impl StacksDevnetApiK8sManager { namespace: &str, ) -> Result { self.ctx.try_log(|logger| { - slog::warn!( + slog::info!( logger, "checking if any devnet assets exist for devnet NAMESPACE: {}", &namespace @@ -346,7 +346,7 @@ impl StacksDevnetApiK8sManager { namespace: &str, ) -> Result { self.ctx.try_log(|logger| { - slog::warn!( + slog::info!( logger, "checking if all devnet assets exist for devnet NAMESPACE: {}", &namespace diff --git a/src/main.rs b/src/main.rs index bf3a6f9..b44b234 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,7 +132,7 @@ async fn handle_request( }; if !exists { let msg = format!("network {} does not exist", &network); - ctx.try_log(|logger| slog::warn!(logger, "{}", msg)); + ctx.try_log(|logger| slog::info!(logger, "{}", msg)); return responder.err_not_found(msg); } From 23e4487ebb8df5af50aa628fc4cec247ecd22e2a Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Fri, 22 Sep 2023 11:10:33 -0400 Subject: [PATCH 11/35] chore: rename storage class name to match infra (#61) --- templates/initial-config/storage-class.yaml | 2 +- templates/stacks-api-pvc.template.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/initial-config/storage-class.yaml b/templates/initial-config/storage-class.yaml index b7a6d1c..cdb3e09 100644 --- a/templates/initial-config/storage-class.yaml +++ b/templates/initial-config/storage-class.yaml @@ -1,7 +1,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - name: devnet-storage-class + name: premium-rwo annotations: openebs.io/cas-type: local cas.openebs.io/config: | diff --git a/templates/stacks-api-pvc.template.yaml b/templates/stacks-api-pvc.template.yaml index cb90749..12f1cad 100644 --- a/templates/stacks-api-pvc.template.yaml +++ b/templates/stacks-api-pvc.template.yaml @@ -11,5 +11,5 @@ spec: storage: 750Mi requests: storage: 500Mi - storageClassName: devnet-storage-class + storageClassName: premium-rwo volumeMode: Filesystem From ddb9ca2dda558a875de1c6d49e460c73117fb2a8 Mon Sep 17 00:00:00 2001 From: Charlie <2747302+CharlieC3@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:11:45 -0400 Subject: [PATCH 12/35] ci: add cd and performance improvements (#64) --- .github/workflows/ci.yaml | 202 +++++++++++++++++++++++++++++++------- 1 file changed, 164 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6c13da4..01cb6be 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,51 +5,50 @@ on: branches: - main - develop - tags-ignore: - - "**" paths-ignore: - - "**/CHANGELOG.md" - - "**/package.json" + - '**/CHANGELOG.md' pull_request: workflow_dispatch: +concurrency: + group: ${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest outputs: - version_output: ${{ steps.meta.outputs.version }} + version: ${{ steps.docker_meta.outputs.version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: - token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} - fetch-depth: 0 persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Docker Meta - id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 + id: docker_meta with: images: | hirosystems/${{ github.event.repository.name }} tags: | type=ref,event=branch type=ref,event=pr - type=semver,pattern={{version}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} - type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} type=raw,value=latest,enable={{is_default_branch}} - name: Create artifact directory run: mkdir -p /tmp/artifacts - name: Build/Save Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . - tags: ${{ steps.meta.outputs.tags }}, - labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.docker_meta.outputs.tags }}, + labels: ${{ steps.docker_meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max outputs: type=docker,dest=/tmp/artifacts/myimage.tar - name: Save docker artifact @@ -61,17 +60,16 @@ jobs: k8s-tests: runs-on: ubuntu-latest needs: build + env: + VERSION: ${{ needs.build.outputs.version }} steps: - name: Read version into env var - env: - version: ${{ needs.build.outputs.version_output }} run: | - echo "version_tag=$version" >> "$GITHUB_ENV" - echo "Extracted version tag: $version_tag" + echo "Extracted version tag: ${VERSION}" - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build k8s cluster uses: nolar/setup-k3d-k3s@v1 @@ -89,7 +87,7 @@ jobs: - name: Load image run: | docker load --input /tmp/artifacts/myimage.tar - docker tag hirosystems/stacks-devnet-api:$version_tag hirosystems/stacks-devnet-api:ci + docker tag hirosystems/stacks-devnet-api:${VERSION} hirosystems/stacks-devnet-api:ci docker image ls -a - name: Deploy k8s manifests @@ -112,8 +110,15 @@ jobs: uses: actions-rs/toolchain@v1 with: toolchain: stable + profile: minimal override: true + - name: Cache cargo + uses: actions/cache@v3 + with: + path: ~/.cargo/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Install and run cargo-tarpaulin run: | cargo install cargo-tarpaulin @@ -121,7 +126,7 @@ jobs: cargo tarpaulin --out lcov - name: Upload to codecov.io - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: token: ${{secrets.CODECOV_TOKEN}} @@ -130,20 +135,20 @@ jobs: needs: - build - k8s-tests - if: needs.build.result == 'success' && needs.k8s-tests.result == 'success' + outputs: + docker_image_digest: ${{ steps.docker_push.outputs.digest }} + new_release_published: ${{ steps.semantic.outputs.new_release_published }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: - token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} - fetch-depth: 0 persist-credentials: false - name: Semantic Release - uses: cycjimmy/semantic-release-action@v3 + uses: cycjimmy/semantic-release-action@v4 id: semantic if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SEMANTIC_RELEASE_PACKAGE: ${{ github.event.repository.name }} with: semantic_version: 19 @@ -152,12 +157,19 @@ jobs: @semantic-release/git@10.0.1 conventional-changelog-conventionalcommits@6.1.0 + - name: Checkout tag + if: steps.semantic.outputs.new_release_version != '' + uses: actions/checkout@v4 + with: + persist-credentials: false + ref: v${{ steps.semantic.outputs.new_release_version }} + - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Docker Meta - id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 + id: docker_meta with: images: | hirosystems/${{ github.event.repository.name }} @@ -168,18 +180,132 @@ jobs: type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} type=raw,value=latest,enable={{is_default_branch}} - - name: Login to DockerHub - uses: docker/login-action@v2 + - name: Log in to DockerHub + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build/Push Image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 + id: docker_push with: context: . - tags: ${{ steps.meta.outputs.tags }}, - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 + tags: ${{ steps.docker_meta.outputs.tags }}, + labels: ${{ steps.docker_meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max # Only push if (there's a new release on main branch, or if building a non-main branch) and (Only run on non-PR events or only PRs that aren't from forks) push: ${{ (github.ref != 'refs/heads/main' || steps.semantic.outputs.new_release_version != '') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }} + + deploy-dev: + runs-on: ubuntu-latest + needs: + - build-publish-release + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + env: + DEPLOY_ENV: dev + environment: + name: Development + url: https://platform.dev.hiro.so/ + steps: + - name: Checkout actions repo + uses: actions/checkout@v4 + with: + ref: main + token: ${{ secrets.GH_TOKEN }} + repository: ${{ secrets.DEVOPS_ACTIONS_REPO }} + + - name: Deploy Stacks Devnet API + uses: ./actions/deploy + with: + docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + k8s_repo: k8s-platform + k8s_branch: main + file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml + gh_token: ${{ secrets.GH_TOKEN }} + + auto-approve-dev: + runs-on: ubuntu-latest + if: needs.build-publish-release.outputs.new_release_published == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + needs: + - build-publish-release + steps: + - name: Approve pending deployment + run: | + sleep 5 + ENV_ID=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') + if [[ -n "${ENV_ID}" ]]; then + curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" + fi + + deploy-staging: + runs-on: ubuntu-latest + needs: + - build-publish-release + - deploy-dev + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + env: + DEPLOY_ENV: stg + environment: + name: Staging + url: https://platform.stg.hiro.so/ + steps: + - name: Checkout actions repo + uses: actions/checkout@v4 + with: + ref: main + token: ${{ secrets.GH_TOKEN }} + repository: ${{ secrets.DEVOPS_ACTIONS_REPO }} + + - name: Deploy Stacks Devnet API + uses: ./actions/deploy + with: + docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + k8s_repo: k8s-platform + k8s_branch: main + file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml + gh_token: ${{ secrets.GH_TOKEN }} + + auto-approve-staging: + runs-on: ubuntu-latest + if: needs.build-publish-release.outputs.new_release_published == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + needs: + - build-publish-release + - deploy-dev + steps: + - name: Approve pending deployment + run: | + sleep 5 + ENV_ID=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') + if [[ -n "${ENV_ID}" ]]; then + curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" + fi + + deploy-prod: + runs-on: ubuntu-latest + if: needs.build-publish-release.outputs.new_release_published == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + needs: + - build-publish-release + - deploy-staging + env: + DEPLOY_ENV: prd + environment: + name: Production + url: https://platform.hiro.so/ + steps: + - name: Checkout actions repo + uses: actions/checkout@v4 + with: + ref: main + token: ${{ secrets.GH_TOKEN }} + repository: ${{ secrets.DEVOPS_ACTIONS_REPO }} + + - name: Deploy Stacks Devnet API + uses: ./actions/deploy + with: + docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + k8s_repo: k8s-platform + k8s_branch: main + file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml + gh_token: ${{ secrets.GH_TOKEN }} From 483fb8b92a4b4b53f3a8169d4b90c989d0757e54 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Fri, 22 Sep 2023 19:07:54 -0400 Subject: [PATCH 13/35] feat: allow config path to be configured via env var (#67) --- src/main.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index b44b234..2db9a14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use stacks_devnet_api::routes::{ handle_new_devnet, handle_try_proxy_service, API_PATH, }; use stacks_devnet_api::{Context, StacksDevnetApiK8sManager}; +use std::env; use std::{convert::Infallible, net::SocketAddr}; #[tokio::main] @@ -24,12 +25,17 @@ async fn main() { tracer: false, }; let k8s_manager = StacksDevnetApiK8sManager::default(&ctx).await; - let config_path = if cfg!(debug_assertions) { - "./Config.toml" - } else { - "/etc/config/Config.toml" + let config_path = match env::var("CONFIG_PATH") { + Ok(path) => path, + Err(_) => { + if cfg!(debug_assertions) { + "./Config.toml".into() + } else { + "/etc/config/Config.toml".into() + } + } }; - let config = ApiConfig::from_path(config_path); + let config = ApiConfig::from_path(&config_path); let make_svc = make_service_fn(|_| { let k8s_manager = k8s_manager.clone(); From d4c2290580c0866164bbd67a6f3ca6ef7c7e17f3 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 25 Sep 2023 10:39:30 -0400 Subject: [PATCH 14/35] feat: status page at `GET /` and `GET /api/v1/status` routes (#68) --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 3 ++ src/main.rs | 8 +++-- src/responder.rs | 65 ++++++++++++++++++++++++++++++++++++--- src/routes.rs | 31 ++++++++++++++----- src/tests/mod.rs | 27 ++++++++++++---- 6 files changed, 114 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 01cb6be..99baff4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -123,7 +123,7 @@ jobs: run: | cargo install cargo-tarpaulin cargo --version - cargo tarpaulin --out lcov + cargo tarpaulin --out lcov --features k8s_tests - name: Upload to codecov.io uses: codecov/codecov-action@v3 diff --git a/Cargo.toml b/Cargo.toml index ec5b592..cb663ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,6 @@ tower-test = "0.4.0" test-case = "3.1.0" rand = "0.8.5" serial_test = "2.0.0" + +[features] +k8s_tests = [] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2db9a14..3ed9209 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use stacks_devnet_api::api_config::ApiConfig; use stacks_devnet_api::responder::Responder; use stacks_devnet_api::routes::{ get_standardized_path_parts, handle_check_devnet, handle_delete_devnet, handle_get_devnet, - handle_new_devnet, handle_try_proxy_service, API_PATH, + handle_get_status, handle_new_devnet, handle_try_proxy_service, API_PATH, }; use stacks_devnet_api::{Context, StacksDevnetApiK8sManager}; use std::env; @@ -78,11 +78,13 @@ async fn handle_request( ) }); let headers = request.headers().clone(); - let responder = Responder::new(http_response_config, headers.clone()).unwrap(); - + let responder = Responder::new(http_response_config, headers.clone(), ctx.clone()).unwrap(); if method == &Method::OPTIONS { return responder.ok(); } + if method == &Method::GET && (path == "/" || path == &format!("{API_PATH}status")) { + return handle_get_status(responder, ctx).await; + } let auth_header = auth_config .auth_header .unwrap_or("x-auth-request-user".to_string()); diff --git a/src/responder.rs b/src/responder.rs index b359c30..5eb1957 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,3 +1,4 @@ +use hiro_system_kit::slog; use hyper::{ header::{ ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, @@ -8,26 +9,39 @@ use hyper::{ }; use std::convert::Infallible; -use crate::api_config::ResponderConfig; +use crate::{api_config::ResponderConfig, Context}; -#[derive(Default)] pub struct Responder { allowed_origins: Vec, allowed_methods: Vec, allowed_headers: String, headers: HeaderMap, + ctx: Context, } +impl Default for Responder { + fn default() -> Self { + Responder { + allowed_origins: Vec::default(), + allowed_methods: Vec::default(), + allowed_headers: String::default(), + headers: HeaderMap::default(), + ctx: Context::empty(), + } + } +} impl Responder { pub fn new( config: ResponderConfig, headers: HeaderMap, + ctx: Context, ) -> Result { Ok(Responder { allowed_origins: config.allowed_origins.unwrap_or_default(), allowed_methods: config.allowed_methods.unwrap_or_default(), allowed_headers: config.allowed_headers.unwrap_or("*".to_string()), headers, + ctx, }) } @@ -60,20 +74,61 @@ impl Responder { fn _respond(&self, code: StatusCode, body: String) -> Result, Infallible> { let builder = self.response_builder(); - match builder.status(code).body(Body::try_from(body).unwrap()) { + let body = match Body::try_from(body) { + Ok(b) => b, + Err(e) => { + self.ctx.try_log(|logger| { + slog::error!( + logger, + "responder failed to create response body: {}", + e.to_string() + ) + }); + Body::empty() + } + }; + match builder.status(code).body(body) { Ok(r) => Ok(r), - Err(_) => unreachable!(), + Err(e) => { + self.ctx.try_log(|logger| { + slog::error!( + logger, + "responder failed to send response: {}", + e.to_string() + ) + }); + Ok(self + .response_builder() + .status(500) + .body(Body::empty()) + .unwrap()) + } } } pub fn respond(&self, code: u16, body: String) -> Result, Infallible> { - self._respond(StatusCode::from_u16(code).unwrap(), body) + self._respond( + StatusCode::from_u16(code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR), + body, + ) } pub fn ok(&self) -> Result, Infallible> { self._respond(StatusCode::OK, "Ok".into()) } + pub fn ok_with_json(&self, body: Body) -> Result, Infallible> { + match self + .response_builder() + .status(StatusCode::OK) + .header("Content-Type", "application/json") + .body(body) + { + Ok(r) => Ok(r), + Err(e) => self.err_internal(format!("failed to send response: {}", e.to_string())), + } + } + pub fn err_method_not_allowed(&self, body: String) -> Result, Infallible> { self._respond(StatusCode::METHOD_NOT_ALLOWED, body) } diff --git a/src/routes.rs b/src/routes.rs index ecb9c02..1d807cf 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,5 +1,6 @@ use hiro_system_kit::slog; -use hyper::{Body, Client, Request, Response, StatusCode, Uri}; +use hyper::{Body, Client, Request, Response, Uri}; +use serde_json::json; use std::{convert::Infallible, str::FromStr}; use crate::{ @@ -9,6 +10,27 @@ use crate::{ Context, StacksDevnetApiK8sManager, }; +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const PRJ_NAME: &str = env!("CARGO_PKG_NAME"); + +pub async fn handle_get_status( + responder: Responder, + ctx: Context, +) -> Result, Infallible> { + let version_info = format!("{PRJ_NAME} v{VERSION}"); + let version_info = json!({ "version": version_info }); + let version_info = match serde_json::to_vec(&version_info) { + Ok(v) => v, + Err(e) => { + let msg = format!("failed to parse version info: {}", e.to_string()); + ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + return responder.err_internal(msg); + } + }; + let body = Body::from(version_info); + responder.ok_with_json(body) +} + pub async fn handle_new_devnet( request: Request, user_id: &str, @@ -60,12 +82,7 @@ pub async fn handle_get_devnet( ) -> Result, Infallible> { match k8s_manager.get_devnet_info(&network).await { Ok(devnet_info) => match serde_json::to_vec(&devnet_info) { - Ok(body) => Ok(responder - .response_builder() - .status(StatusCode::OK) - .header("Content-Type", "application/json") - .body(Body::from(body)) - .unwrap()), + Ok(body) => responder.ok_with_json(Body::from(body)), Err(e) => { let msg = format!( "failed to form response body: NAMESPACE: {}, ERROR: {}", diff --git a/src/tests/mod.rs b/src/tests/mod.rs index ffc8b2d..deac41d 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -26,6 +26,11 @@ use stacks_devnet_api::{ use test_case::test_case; use tower_test::mock::{self, Handle}; +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const PRJ_NAME: &str = env!("CARGO_PKG_NAME"); +fn get_version_info() -> String { + format!("{{\"version\":\"{PRJ_NAME} v{VERSION}\"}}") +} fn get_template_config() -> StacksDevnetConfig { let file_path = "src/tests/fixtures/stacks-devnet-config.json"; let file = File::open(file_path) @@ -115,6 +120,7 @@ enum TestBody { #[test_case("/api/v1/network/{namespace}/stacks-node/v2/info/", Method::GET, None, true => using assert_failed_proxy; "proxies requests to downstream nodes")] #[serial_test::serial] #[tokio::test] +#[cfg_attr(not(feature = "k8s_tests"), ignore)] async fn it_responds_to_valid_requests_with_deploy( mut request_path: &str, method: Method, @@ -175,11 +181,14 @@ async fn it_responds_to_valid_requests_with_deploy( } #[test_case("any", Method::OPTIONS, false => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for any OPTIONS request")] +#[test_case("/", Method::GET, false => is equal_to (StatusCode::OK, get_version_info()); "200 for GET /")] +#[test_case("/api/v1/status", Method::GET, false => is equal_to (StatusCode::OK, get_version_info()); "200 for GET /api/v1/status")] #[test_case("/api/v1/network/{namespace}", Method::DELETE, true => using assert_cannot_delete_devnet_err; "409 for network DELETE request to non-existing network")] #[test_case("/api/v1/network/{namespace}", Method::GET, true => using assert_not_all_assets_exist_err; "404 for network GET request to non-existing network")] #[test_case("/api/v1/network/{namespace}", Method::HEAD, true => is equal_to (StatusCode::NOT_FOUND, "not found".to_string()); "404 for network HEAD request to non-existing network")] #[test_case("/api/v1/network/{namespace}/stacks-node/v2/info/", Method::GET, true => using assert_not_all_assets_exist_err; "404 for proxy requests to downstream nodes of non-existing network")] #[tokio::test] +#[cfg_attr(not(feature = "k8s_tests"), ignore)] async fn it_responds_to_valid_requests( mut request_path: &str, method: Method, @@ -328,15 +337,16 @@ async fn it_responds_to_invalid_request_header() { assert_eq!(body_str, "missing required auth header".to_string()); } +#[test_case("/api/v1/network/test", Method::OPTIONS => is equal_to "Ok".to_string())] +#[test_case("/api/v1/status", Method::GET => is equal_to get_version_info() )] +#[test_case("/", Method::GET => is equal_to get_version_info())] #[tokio::test] -async fn it_ignores_request_header_for_options_requests() { +async fn it_ignores_request_header_for_some_requests(request_path: &str, method: Method) -> String { let (k8s_manager, ctx) = get_mock_k8s_manager().await; - let request_builder = Request::builder() - .uri("/api/v1/network/test") - .method(Method::OPTIONS); + let request_builder = Request::builder().uri(request_path).method(method); let request: Request = request_builder.body(Body::empty()).unwrap(); - let response = handle_request( + let mut response = handle_request( request, k8s_manager.clone(), ApiConfig::default(), @@ -345,6 +355,10 @@ async fn it_ignores_request_header_for_options_requests() { .await .unwrap(); assert_eq!(response.status(), 200); + let body = response.body_mut(); + let bytes = body::to_bytes(body).await.unwrap().to_vec(); + let body_str = String::from_utf8(bytes).unwrap(); + body_str } #[test_case("" => is equal_to PathParts { route: String::new(), ..Default::default() }; "for empty path")] @@ -399,7 +413,7 @@ fn responder_allows_configuring_allowed_origins() { }; let mut headers = HeaderMap::new(); headers.append("ORIGIN", HeaderValue::from_str("example.com").unwrap()); - let responder = Responder::new(config, headers).unwrap(); + let responder = Responder::new(config, headers, Context::empty()).unwrap(); let builder = responder.response_builder(); let built_headers = builder.headers_ref().unwrap(); assert_eq!(built_headers.get(ACCESS_CONTROL_ALLOW_ORIGIN).unwrap(), "*"); @@ -411,6 +425,7 @@ fn responder_allows_configuring_allowed_origins() { #[serial_test::serial] #[tokio::test] +#[cfg_attr(not(feature = "k8s_tests"), ignore)] async fn namespace_prefix_config_prepends_header() { let (k8s_manager, ctx) = get_k8s_manager().await; From cb57c327873fc8281ce55deb72b961d18945d05f Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 25 Sep 2023 14:31:53 -0400 Subject: [PATCH 15/35] breaking: rename assets (#69) --- .github/workflows/ci.yaml | 2 +- README.md | 8 +-- scripts/deploy-api.sh | 2 +- scripts/get-logs.sh | 10 ++-- scripts/redeploy-api.sh | 2 +- src/config.rs | 10 ++-- src/lib.rs | 56 +++++++++---------- src/resources/configmap.rs | 24 ++++---- src/resources/pod.rs | 8 +-- src/resources/pvc.rs | 4 +- src/resources/service.rs | 28 +++++----- src/resources/tests.rs | 49 ++++++++-------- src/template_parser.rs | 49 ++++++++-------- src/tests/mod.rs | 10 ++-- ...d-project-manifest-configmap.template.yaml | 7 --- templates/ci/stacks-devnet-api.template.yaml | 18 +++--- .../bitcoind.template.yaml} | 2 +- ...chain-coord-deployment-plan.template.yaml} | 2 +- .../chain-coord-devnet.template.yaml} | 2 +- .../chain-coord-project-dir.template.yaml} | 2 +- ...hain-coord-project-manifest.template.yaml} | 2 +- .../stacks-blockchain-api-pg.template.yaml | 7 +++ .../stacks-blockchain-api.template.yaml} | 2 +- .../stacks-blockchain.template.yaml} | 2 +- .../bitcoind-chain-coordinator.template.yaml} | 40 ++++++------- .../stacks-blockchain-api.template.yaml} | 18 +++--- .../stacks-blockchain.template.yaml} | 16 +++--- .../stacks-blockchain-api-pg.template.yaml} | 2 +- .../bitcoind-chain-coordinator.template.yaml} | 2 +- .../stacks-blockchain-api.template.yaml} | 4 +- .../stacks-blockchain.template.yaml} | 4 +- ...tacks-api-postgres-configmap.template.yaml | 7 --- templates/stacks-devnet-api.template.yaml | 18 +++--- 33 files changed, 202 insertions(+), 217 deletions(-) delete mode 100644 templates/chain-coord-project-manifest-configmap.template.yaml rename templates/{bitcoind-configmap.template.yaml => configmaps/bitcoind.template.yaml} (81%) rename templates/{stacks-api-configmap.template.yaml => configmaps/chain-coord-deployment-plan.template.yaml} (79%) rename templates/{chain-coord-devnet-configmap.template.yaml => configmaps/chain-coord-devnet.template.yaml} (81%) rename templates/{chain-coord-namespace-configmap.template.yaml => configmaps/chain-coord-project-dir.template.yaml} (79%) rename templates/{chain-coord-project-dir-configmap.template.yaml => configmaps/chain-coord-project-manifest.template.yaml} (78%) create mode 100644 templates/configmaps/stacks-blockchain-api-pg.template.yaml rename templates/{chain-coord-deployment-plan-configmap.template.yaml => configmaps/stacks-blockchain-api.template.yaml} (75%) rename templates/{stacks-node-configmap.template.yaml => configmaps/stacks-blockchain.template.yaml} (79%) rename templates/{bitcoind-chain-coordinator-pod.template.yaml => pods/bitcoind-chain-coordinator.template.yaml} (70%) rename templates/{stacks-api-pod.template.yaml => pods/stacks-blockchain-api.template.yaml} (68%) rename templates/{stacks-node-pod.template.yaml => pods/stacks-blockchain.template.yaml} (69%) rename templates/{stacks-api-pvc.template.yaml => pvcs/stacks-blockchain-api-pg.template.yaml} (88%) rename templates/{bitcoind-chain-coordinator-service.template.yaml => services/bitcoind-chain-coordinator.template.yaml} (90%) rename templates/{stacks-api-service.template.yaml => services/stacks-blockchain-api.template.yaml} (83%) rename templates/{stacks-node-service.template.yaml => services/stacks-blockchain.template.yaml} (77%) delete mode 100644 templates/stacks-api-postgres-configmap.template.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 99baff4..9880335 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -94,7 +94,7 @@ jobs: run: | k3d image import hirosystems/stacks-devnet-api:ci -c k3d-kube kubectl create namespace devnet - kubectl create configmap stacks-devnet-api-conf --from-file=./Config.toml --namespace devnet + kubectl create configmap stacks-devnet-api --from-file=./Config.toml --namespace devnet kubectl apply -f ./templates/ci/stacks-devnet-api.template.yaml echo "sleep for 30 sec" sleep 30 diff --git a/README.md b/README.md index e04d256..c9662b5 100644 --- a/README.md +++ b/README.md @@ -47,11 +47,11 @@ metadata: name: stacks-devnet-api namespace: devnet spec: - serviceAccountName: stacks-devnet-api-service-account + serviceAccountName: stacks-devnet-api containers: - command: - ./stacks-devnet-api - name: stacks-devnet-api-container + name: stacks-devnet-api - image: quay.io/hirosystems/stacks-devnet-api:latest - imagePullPolicy: Always + image: stacks-devnet-api:latest @@ -83,9 +83,9 @@ When the service has been deployed to your Kubernetes cluster, it should be reac "bitcoin_chain_tip": 116 } ``` - - `GET/POST localhost:8477/api/v1/network//stacks-node/*` - Forwards `*` to the underlying stacks node pod of the devnet. If not all devnet assets exist for the given namespace, a 404 error will be returned. + - `GET/POST localhost:8477/api/v1/network//stacks-blockchain/*` - Forwards `*` to the underlying stacks node pod of the devnet. If not all devnet assets exist for the given namespace, a 404 error will be returned. - `GET/POST localhost:8477/api/v1/network//bitcoin-node/*` - Forwards `*` to the underlying bitcoin node pod of the devnet. If not all devnet assets exist for the given namespace, a 404 error will be returned. -- `GET/POST localhost:8477/api/v1/network//stacks-api/*` - Forwards `*` to the underlying stacks api pod of the devnet. If not all devnet assets exist for the given namespace, a 404 error will be returned. +- `GET/POST localhost:8477/api/v1/network//stacks-blockchain-api/*` - Forwards `*` to the underlying stacks api pod of the devnet. If not all devnet assets exist for the given namespace, a 404 error will be returned. ## Bugs and feature requests diff --git a/scripts/deploy-api.sh b/scripts/deploy-api.sh index 2fcf312..949c424 100755 --- a/scripts/deploy-api.sh +++ b/scripts/deploy-api.sh @@ -1,3 +1,3 @@ kubectl --context kind-kind create namespace devnet -kubectl --context kind-kind create configmap stacks-devnet-api-conf --from-file=./Config.toml --namespace devnet && \ +kubectl --context kind-kind create configmap stacks-devnet-api --from-file=./Config.toml --namespace devnet && \ kubectl --context kind-kind apply -f ./templates/stacks-devnet-api.template.yaml diff --git a/scripts/get-logs.sh b/scripts/get-logs.sh index 3f88f54..05302ec 100755 --- a/scripts/get-logs.sh +++ b/scripts/get-logs.sh @@ -1,5 +1,5 @@ -kubectl --context kind-kind logs stacks-node --namespace $1 > ./logs/stacks-node.txt & \ -kubectl --context kind-kind logs stacks-api --namespace $1 -c stacks-api-container > ./logs/stacks-api.txt & \ -kubectl --context kind-kind logs stacks-api --namespace $1 -c stacks-api-postgres > ./logs/stacks-api-postgres.txt & \ -kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c bitcoind-container > ./logs/bitcoin-node.txt & \ -kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c chain-coordinator-container > ./logs/chain-coordinator.txt \ No newline at end of file +kubectl --context kind-kind logs stacks-blockchain --namespace $1 > ./logs/stacks-blockchain.txt & \ +kubectl --context kind-kind logs stacks-blockchain-api --namespace $1 -c stacks-blockchain-api > ./logs/stack-blockchains-api.txt & \ +kubectl --context kind-kind logs stacks-blockchain-api --namespace $1 -c postgres > ./logs/stacks-blockchain-api-pg.txt & \ +kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c bitcoind > ./logs/bitcoin-node.txt & \ +kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c chain-coordinator > ./logs/chain-coordinator.txt \ No newline at end of file diff --git a/scripts/redeploy-api.sh b/scripts/redeploy-api.sh index 4654338..d50855e 100755 --- a/scripts/redeploy-api.sh +++ b/scripts/redeploy-api.sh @@ -1,3 +1,3 @@ -kubectl --context kind-kind delete configmap stacks-devnet-api-conf --namespace devnet & \ +kubectl --context kind-kind delete configmap stacks-devnet-api --namespace devnet & \ kubectl --context kind-kind delete pod stacks-devnet-api --namespace devnet && \ ./scripts/deploy-api.sh diff --git a/src/config.rs b/src/config.rs index a15a36b..465b799 100644 --- a/src/config.rs +++ b/src/config.rs @@ -113,27 +113,27 @@ impl StacksDevnetConfig { .parse::() .unwrap(); devnet_config.stacks_node_p2p_port = - get_service_port(StacksDevnetService::StacksNode, ServicePort::P2P) + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::P2P) .unwrap() .parse::() .unwrap(); devnet_config.stacks_node_rpc_port = - get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC) + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC) .unwrap() .parse::() .unwrap(); devnet_config.stacks_api_port = - get_service_port(StacksDevnetService::StacksApi, ServicePort::API) + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::API) .unwrap() .parse::() .unwrap(); devnet_config.stacks_api_events_port = - get_service_port(StacksDevnetService::StacksApi, ServicePort::Event) + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::Event) .unwrap() .parse::() .unwrap(); devnet_config.postgres_port = - get_service_port(StacksDevnetService::StacksApi, ServicePort::DB) + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::DB) .unwrap() .parse::() .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 40d94c3..b710789 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -437,8 +437,9 @@ impl StacksDevnetApiK8sManager { namespace: &str, ) -> Result { let client = HttpClient::new(); - let url = get_service_url(namespace, StacksDevnetService::StacksNode); - let port = get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC).unwrap(); + let url = get_service_url(namespace, StacksDevnetService::StacksBlockchain); + let port = + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(); let url = format!("http://{}:{}/v2/info", url, port); let context = format!("NAMESPACE: {}", namespace); @@ -548,8 +549,8 @@ impl StacksDevnetApiK8sManager { chain_info, ) = try_join4( self.get_pod_status_info(&namespace, StacksDevnetPod::BitcoindNode), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksNode), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksApi), + self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchain), + self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchainApi), self.get_stacks_v2_info(&namespace), ) .await?; @@ -832,13 +833,6 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_configmap( - StacksDevnetConfigmap::Namespace, - &namespace, - Some(vec![("NAMESPACE".into(), namespace.clone())]), - ) - .await?; - self.deploy_configmap( StacksDevnetConfigmap::ProjectManifest, &namespace, @@ -931,8 +925,8 @@ impl StacksDevnetApiK8sManager { block_reward_recipient = "{}" # microblock_attempt_time_ms = 15000 "#, - get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC).unwrap(), - get_service_port(StacksDevnetService::StacksNode, ServicePort::P2P).unwrap(), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::P2P).unwrap(), stacks_miner_secret_key_hex, stacks_miner_secret_key_hex, devnet_config.stacks_node_wait_time_for_microblocks, @@ -979,15 +973,16 @@ impl StacksDevnetApiK8sManager { stacks_conf.push_str(&format!( r#" - # Add stacks-api as an event observer + # Add stacks-blockchain-api as an event observer [[events_observer]] endpoint = "{}:{}" retry_count = 255 include_data_events = false events_keys = ["*"] "#, - get_service_url(&namespace, StacksDevnetService::StacksApi), - get_service_port(StacksDevnetService::StacksApi, ServicePort::Event).unwrap(), + get_service_url(&namespace, StacksDevnetService::StacksBlockchainApi), + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::Event) + .unwrap(), )); stacks_conf.push_str(&format!( @@ -1046,16 +1041,16 @@ impl StacksDevnetApiK8sManager { }; self.deploy_configmap( - StacksDevnetConfigmap::StacksNode, + StacksDevnetConfigmap::StacksBlockchain, &namespace, Some(vec![("Stacks.toml".into(), stacks_conf)]), ) .await?; - self.deploy_pod(StacksDevnetPod::StacksNode, &namespace) + self.deploy_pod(StacksDevnetPod::StacksBlockchain, &namespace) .await?; - self.deploy_service(StacksDevnetService::StacksNode, namespace) + self.deploy_service(StacksDevnetService::StacksBlockchain, namespace) .await?; Ok(()) @@ -1068,19 +1063,22 @@ impl StacksDevnetApiK8sManager { ("POSTGRES_DB".into(), "stacks_api".into()), ]); self.deploy_configmap( - StacksDevnetConfigmap::StacksApiPostgres, + StacksDevnetConfigmap::StacksBlockchainApiPg, &namespace, Some(stacks_api_pg_env), ) .await?; // configmap env vars for api conatainer - let stacks_node_host = get_service_url(&namespace, StacksDevnetService::StacksNode); - let rpc_port = get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC).unwrap(); - let api_port = get_service_port(StacksDevnetService::StacksApi, ServicePort::API).unwrap(); + let stacks_node_host = get_service_url(&namespace, StacksDevnetService::StacksBlockchain); + let rpc_port = + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(); + let api_port = + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::API).unwrap(); let event_port = - get_service_port(StacksDevnetService::StacksApi, ServicePort::Event).unwrap(); - let db_port = get_service_port(StacksDevnetService::StacksApi, ServicePort::DB).unwrap(); + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::Event).unwrap(); + let db_port = + get_service_port(StacksDevnetService::StacksBlockchainApi, ServicePort::DB).unwrap(); let stacks_api_env = Vec::from([ ("STACKS_CORE_RPC_HOST".into(), stacks_node_host), ("STACKS_BLOCKCHAIN_API_DB".into(), "pg".into()), @@ -1101,19 +1099,19 @@ impl StacksDevnetApiK8sManager { ("STACKS_API_LOG_LEVEL".into(), "debug".into()), ]); self.deploy_configmap( - StacksDevnetConfigmap::StacksApi, + StacksDevnetConfigmap::StacksBlockchainApi, &namespace, Some(stacks_api_env), ) .await?; - self.deploy_pvc(StacksDevnetPvc::StacksApi, &namespace) + self.deploy_pvc(StacksDevnetPvc::StacksBlockchainApiPg, &namespace) .await?; - self.deploy_pod(StacksDevnetPod::StacksApi, &namespace) + self.deploy_pod(StacksDevnetPod::StacksBlockchainApi, &namespace) .await?; - self.deploy_service(StacksDevnetService::StacksApi, &namespace) + self.deploy_service(StacksDevnetService::StacksBlockchainApi, &namespace) .await?; Ok(()) diff --git a/src/resources/configmap.rs b/src/resources/configmap.rs index 4658d28..773c172 100644 --- a/src/resources/configmap.rs +++ b/src/resources/configmap.rs @@ -4,28 +4,26 @@ use strum_macros::EnumIter; #[derive(EnumIter, Debug)] pub enum StacksDevnetConfigmap { BitcoindNode, - StacksNode, - StacksApi, - StacksApiPostgres, + StacksBlockchain, + StacksBlockchainApi, + StacksBlockchainApiPg, DeploymentPlan, Devnet, ProjectDir, - Namespace, ProjectManifest, } impl fmt::Display for StacksDevnetConfigmap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - StacksDevnetConfigmap::BitcoindNode => write!(f, "bitcoind-conf"), - StacksDevnetConfigmap::StacksNode => write!(f, "stacks-node-conf"), - StacksDevnetConfigmap::StacksApi => write!(f, "stacks-api-conf"), - StacksDevnetConfigmap::StacksApiPostgres => write!(f, "stacks-api-postgres-conf"), - StacksDevnetConfigmap::DeploymentPlan => write!(f, "deployment-plan-conf"), - StacksDevnetConfigmap::Devnet => write!(f, "devnet-conf"), - StacksDevnetConfigmap::ProjectDir => write!(f, "project-dir-conf"), - StacksDevnetConfigmap::Namespace => write!(f, "namespace-conf"), - StacksDevnetConfigmap::ProjectManifest => write!(f, "project-manifest-conf"), + StacksDevnetConfigmap::BitcoindNode => write!(f, "bitcoind"), + StacksDevnetConfigmap::StacksBlockchain => write!(f, "stacks-blockchain"), + StacksDevnetConfigmap::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + StacksDevnetConfigmap::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api-pg"), + StacksDevnetConfigmap::DeploymentPlan => write!(f, "deployment-plan"), + StacksDevnetConfigmap::Devnet => write!(f, "devnet"), + StacksDevnetConfigmap::ProjectDir => write!(f, "project-dir"), + StacksDevnetConfigmap::ProjectManifest => write!(f, "project-manifest"), } } } diff --git a/src/resources/pod.rs b/src/resources/pod.rs index e04cab8..4270a79 100644 --- a/src/resources/pod.rs +++ b/src/resources/pod.rs @@ -4,16 +4,16 @@ use strum_macros::EnumIter; #[derive(EnumIter, Debug)] pub enum StacksDevnetPod { BitcoindNode, - StacksNode, - StacksApi, + StacksBlockchain, + StacksBlockchainApi, } impl fmt::Display for StacksDevnetPod { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StacksDevnetPod::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), - StacksDevnetPod::StacksNode => write!(f, "stacks-node"), - StacksDevnetPod::StacksApi => write!(f, "stacks-api"), + StacksDevnetPod::StacksBlockchain => write!(f, "stacks-blockchain"), + StacksDevnetPod::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), } } } diff --git a/src/resources/pvc.rs b/src/resources/pvc.rs index 21ad26c..81605e8 100644 --- a/src/resources/pvc.rs +++ b/src/resources/pvc.rs @@ -3,13 +3,13 @@ use strum_macros::EnumIter; #[derive(EnumIter, Debug)] pub enum StacksDevnetPvc { - StacksApi, + StacksBlockchainApiPg, } impl fmt::Display for StacksDevnetPvc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - StacksDevnetPvc::StacksApi => write!(f, "stacks-api-pvc"), + StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api-pg"), } } } diff --git a/src/resources/service.rs b/src/resources/service.rs index 8ecc83b..852e125 100644 --- a/src/resources/service.rs +++ b/src/resources/service.rs @@ -4,8 +4,8 @@ use strum_macros::EnumIter; #[derive(EnumIter, Debug, Clone, PartialEq)] pub enum StacksDevnetService { BitcoindNode, - StacksNode, - StacksApi, + StacksBlockchain, + StacksBlockchainApi, } pub enum ServicePort { @@ -21,9 +21,9 @@ pub enum ServicePort { impl fmt::Display for StacksDevnetService { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - StacksDevnetService::BitcoindNode => write!(f, "bitcoind-chain-coordinator-service"), - StacksDevnetService::StacksNode => write!(f, "stacks-node-service"), - StacksDevnetService::StacksApi => write!(f, "stacks-api-service"), + StacksDevnetService::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), + StacksDevnetService::StacksBlockchain => write!(f, "stacks-blockchain"), + StacksDevnetService::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), } } } @@ -34,21 +34,21 @@ pub fn get_service_port(service: StacksDevnetService, port_type: ServicePort) -> (StacksDevnetService::BitcoindNode, ServicePort::P2P) => Some("18444".into()), (StacksDevnetService::BitcoindNode, ServicePort::Ingestion) => Some("20445".into()), (StacksDevnetService::BitcoindNode, ServicePort::Control) => Some("20446".into()), - (StacksDevnetService::StacksNode, ServicePort::RPC) => Some("20443".into()), - (StacksDevnetService::StacksNode, ServicePort::P2P) => Some("20444".into()), - (StacksDevnetService::StacksApi, ServicePort::API) => Some("3999".into()), - (StacksDevnetService::StacksApi, ServicePort::Event) => Some("3700".into()), - (StacksDevnetService::StacksApi, ServicePort::DB) => Some("5432".into()), + (StacksDevnetService::StacksBlockchain, ServicePort::RPC) => Some("20443".into()), + (StacksDevnetService::StacksBlockchain, ServicePort::P2P) => Some("20444".into()), + (StacksDevnetService::StacksBlockchainApi, ServicePort::API) => Some("3999".into()), + (StacksDevnetService::StacksBlockchainApi, ServicePort::Event) => Some("3700".into()), + (StacksDevnetService::StacksBlockchainApi, ServicePort::DB) => Some("5432".into()), (_, _) => None, } } pub fn get_user_facing_port(service: StacksDevnetService) -> Option { match service { - StacksDevnetService::BitcoindNode | StacksDevnetService::StacksNode => { + StacksDevnetService::BitcoindNode | StacksDevnetService::StacksBlockchain => { get_service_port(service, ServicePort::RPC) } - StacksDevnetService::StacksApi => get_service_port(service, ServicePort::API), + StacksDevnetService::StacksBlockchainApi => get_service_port(service, ServicePort::API), } } @@ -59,8 +59,8 @@ pub fn get_service_url(namespace: &str, service: StacksDevnetService) -> String pub fn get_service_from_path_part(path_part: &str) -> Option { match path_part { "bitcoin-node" => Some(StacksDevnetService::BitcoindNode), - "stacks-node" => Some(StacksDevnetService::StacksNode), - "stacks-api" => Some(StacksDevnetService::StacksApi), + "stacks-blockchain" => Some(StacksDevnetService::StacksBlockchain), + "stacks-blockchain-api" => Some(StacksDevnetService::StacksBlockchainApi), _ => None, } } diff --git a/src/resources/tests.rs b/src/resources/tests.rs index 9e3a959..c0ff4d0 100644 --- a/src/resources/tests.rs +++ b/src/resources/tests.rs @@ -5,34 +5,33 @@ use super::{ }; use test_case::test_case; -#[test_case(StacksDevnetConfigmap::BitcoindNode => is equal_to "bitcoind-conf".to_string(); "for BitcoinNode")] -#[test_case(StacksDevnetConfigmap::StacksNode => is equal_to "stacks-node-conf".to_string(); "for StacksNode")] -#[test_case(StacksDevnetConfigmap::StacksApi => is equal_to "stacks-api-conf".to_string(); "for StacksApi")] -#[test_case(StacksDevnetConfigmap::StacksApiPostgres => is equal_to "stacks-api-postgres-conf".to_string(); "for StacksApiPostgres")] -#[test_case(StacksDevnetConfigmap::DeploymentPlan => is equal_to "deployment-plan-conf".to_string(); "for DeploymentPlan")] -#[test_case(StacksDevnetConfigmap::Devnet => is equal_to "devnet-conf".to_string(); "for Devnet")] -#[test_case(StacksDevnetConfigmap::ProjectDir => is equal_to "project-dir-conf".to_string(); "for ProjectDir")] -#[test_case(StacksDevnetConfigmap::Namespace => is equal_to "namespace-conf".to_string(); "for Namespace")] -#[test_case(StacksDevnetConfigmap::ProjectManifest => is equal_to "project-manifest-conf".to_string(); "for ProjectManifest")] +#[test_case(StacksDevnetConfigmap::BitcoindNode => is equal_to "bitcoind".to_string(); "for BitcoinNode")] +#[test_case(StacksDevnetConfigmap::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] +#[test_case(StacksDevnetConfigmap::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] +#[test_case(StacksDevnetConfigmap::StacksBlockchainApiPg => is equal_to "stacks-blockchain-api-pg".to_string(); "for StacksBlockchainApiPg")] +#[test_case(StacksDevnetConfigmap::DeploymentPlan => is equal_to "deployment-plan".to_string(); "for DeploymentPlan")] +#[test_case(StacksDevnetConfigmap::Devnet => is equal_to "devnet".to_string(); "for Devnet")] +#[test_case(StacksDevnetConfigmap::ProjectDir => is equal_to "project-dir".to_string(); "for ProjectDir")] +#[test_case(StacksDevnetConfigmap::ProjectManifest => is equal_to "project-manifest".to_string(); "for ProjectManifest")] fn it_prints_correct_name_for_configmap(configmap: StacksDevnetConfigmap) -> String { configmap.to_string() } #[test_case(StacksDevnetPod::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] -#[test_case(StacksDevnetPod::StacksNode => is equal_to "stacks-node".to_string(); "for StacksNode")] -#[test_case(StacksDevnetPod::StacksApi => is equal_to "stacks-api".to_string(); "for StacksApi")] +#[test_case(StacksDevnetPod::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] +#[test_case(StacksDevnetPod::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] fn it_prints_correct_name_for_pod(pod: StacksDevnetPod) -> String { pod.to_string() } -#[test_case(StacksDevnetPvc::StacksApi => is equal_to "stacks-api-pvc".to_string(); "for StacksApi")] +#[test_case(StacksDevnetPvc::StacksBlockchainApiPg => is equal_to "stacks-blockchain-api-pg".to_string(); "for StacksBlockchainApiPg")] fn it_prints_correct_name_for_pvc(pvc: StacksDevnetPvc) -> String { pvc.to_string() } -#[test_case(StacksDevnetService::BitcoindNode => is equal_to "bitcoind-chain-coordinator-service".to_string(); "for BitcoindNode")] -#[test_case(StacksDevnetService::StacksNode => is equal_to "stacks-node-service".to_string(); "for StacksNode")] -#[test_case(StacksDevnetService::StacksApi => is equal_to "stacks-api-service".to_string(); "for StacksApi")] +#[test_case(StacksDevnetService::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] +#[test_case(StacksDevnetService::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] +#[test_case(StacksDevnetService::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] fn it_prints_correct_name_for_service(service: StacksDevnetService) -> String { service.to_string() } @@ -41,12 +40,12 @@ fn it_prints_correct_name_for_service(service: StacksDevnetService) -> String { #[test_case(StacksDevnetService::BitcoindNode, ServicePort::P2P => is equal_to Some("18444".to_string()); "for BitcoindNode P2P port")] #[test_case(StacksDevnetService::BitcoindNode, ServicePort::Ingestion => is equal_to Some("20445".to_string()); "for BitcoindNode Ingestion port")] #[test_case(StacksDevnetService::BitcoindNode, ServicePort::Control => is equal_to Some("20446".to_string()); "for BitcoindNode Control port")] -#[test_case(StacksDevnetService::StacksNode, ServicePort::RPC => is equal_to Some("20443".to_string()); "for StacksNode RPC port")] -#[test_case(StacksDevnetService::StacksNode, ServicePort::P2P => is equal_to Some("20444".to_string()); "for StacksNode P2P port")] -#[test_case(StacksDevnetService::StacksApi, ServicePort::API => is equal_to Some("3999".to_string()); "for StacksApi API port")] -#[test_case(StacksDevnetService::StacksApi, ServicePort::Event => is equal_to Some("3700".to_string()); "for StacksApi Event port")] -#[test_case(StacksDevnetService::StacksApi, ServicePort::DB => is equal_to Some("5432".to_string()); "for StacksApi DB port")] -#[test_case(StacksDevnetService::StacksApi, ServicePort::RPC => is equal_to None; "invalid service port combination")] +#[test_case(StacksDevnetService::StacksBlockchain, ServicePort::RPC => is equal_to Some("20443".to_string()); "for StacksBlockchain RPC port")] +#[test_case(StacksDevnetService::StacksBlockchain, ServicePort::P2P => is equal_to Some("20444".to_string()); "for StacksBlockchain P2P port")] +#[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::API => is equal_to Some("3999".to_string()); "for StacksBlockchainApi API port")] +#[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::Event => is equal_to Some("3700".to_string()); "for StacksBlockchainApi Event port")] +#[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::DB => is equal_to Some("5432".to_string()); "for StacksBlockchainApi DB port")] +#[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::RPC => is equal_to None; "invalid service port combination")] fn it_gets_correct_port_for_service( service: StacksDevnetService, port_type: ServicePort, @@ -55,16 +54,16 @@ fn it_gets_correct_port_for_service( } #[test_case("bitcoin-node" => is equal_to Some(StacksDevnetService::BitcoindNode); "for bitcoin-node")] -#[test_case("stacks-node" => is equal_to Some(StacksDevnetService::StacksNode); "for stacks-node")] -#[test_case("stacks-api" => is equal_to Some(StacksDevnetService::StacksApi); "for stacks-api")] +#[test_case("stacks-blockchain" => is equal_to Some(StacksDevnetService::StacksBlockchain); "for stacks-blockchain")] +#[test_case("stacks-blockchain-api" => is equal_to Some(StacksDevnetService::StacksBlockchainApi); "for stacks-blockchain-api")] #[test_case("invalid" => is equal_to None; "returning None for invalid paths")] fn it_prints_service_from_path_part(path_part: &str) -> Option { get_service_from_path_part(path_part) } #[test_case(StacksDevnetService::BitcoindNode => is equal_to Some("18443".to_string()); "for BitcoindNode")] -#[test_case(StacksDevnetService::StacksNode => is equal_to Some("20443".to_string()); "for StacksNode")] -#[test_case(StacksDevnetService::StacksApi => is equal_to Some("3999".to_string()); "for StacksApi")] +#[test_case(StacksDevnetService::StacksBlockchain => is equal_to Some("20443".to_string()); "for StacksBlockchain")] +#[test_case(StacksDevnetService::StacksBlockchainApi => is equal_to Some("3999".to_string()); "for StacksBlockchainApi")] fn it_gets_user_facing_port_for_service(service: StacksDevnetService) -> Option { get_user_facing_port(service) } diff --git a/src/template_parser.rs b/src/template_parser.rs index 9e63384..521662f 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -6,52 +6,49 @@ use crate::resources::{ pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { match resource { StacksDevnetResource::Pod(StacksDevnetPod::BitcoindNode) => { - include_str!("../templates/bitcoind-chain-coordinator-pod.template.yaml") + include_str!("../templates/pods/bitcoind-chain-coordinator.template.yaml") } StacksDevnetResource::Service(StacksDevnetService::BitcoindNode) => { - include_str!("../templates/bitcoind-chain-coordinator-service.template.yaml") + include_str!("../templates/services/bitcoind-chain-coordinator.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::BitcoindNode) => { - include_str!("../templates/bitcoind-configmap.template.yaml") + include_str!("../templates/configmaps/bitcoind.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::DeploymentPlan) => { - include_str!("../templates/chain-coord-deployment-plan-configmap.template.yaml") + include_str!("../templates/configmaps/chain-coord-deployment-plan.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::Devnet) => { - include_str!("../templates/chain-coord-devnet-configmap.template.yaml") - } - StacksDevnetResource::Configmap(StacksDevnetConfigmap::Namespace) => { - include_str!("../templates/chain-coord-namespace-configmap.template.yaml") + include_str!("../templates/configmaps/chain-coord-devnet.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::ProjectDir) => { - include_str!("../templates/chain-coord-project-dir-configmap.template.yaml") + include_str!("../templates/configmaps/chain-coord-project-dir.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::ProjectManifest) => { - include_str!("../templates/chain-coord-project-manifest-configmap.template.yaml") + include_str!("../templates/configmaps/chain-coord-project-manifest.template.yaml") } - StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksApi) => { - include_str!("../templates/stacks-api-configmap.template.yaml") + StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApi) => { + include_str!("../templates/configmaps/stacks-blockchain-api.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksApi) => { - include_str!("../templates/stacks-api-pod.template.yaml") + StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchainApi) => { + include_str!("../templates/pods/stacks-blockchain-api.template.yaml") } - StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksApiPostgres) => { - include_str!("../templates/stacks-api-postgres-configmap.template.yaml") + StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApiPg) => { + include_str!("../templates/configmaps/stacks-blockchain-api-pg.template.yaml") } - StacksDevnetResource::Pvc(StacksDevnetPvc::StacksApi) => { - include_str!("../templates/stacks-api-pvc.template.yaml") + StacksDevnetResource::Pvc(StacksDevnetPvc::StacksBlockchainApiPg) => { + include_str!("../templates/pvcs/stacks-blockchain-api-pg.template.yaml") } - StacksDevnetResource::Service(StacksDevnetService::StacksApi) => { - include_str!("../templates/stacks-api-service.template.yaml") + StacksDevnetResource::Service(StacksDevnetService::StacksBlockchainApi) => { + include_str!("../templates/services/stacks-blockchain-api.template.yaml") } - StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksNode) => { - include_str!("../templates/stacks-node-configmap.template.yaml") + StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchain) => { + include_str!("../templates/configmaps/stacks-blockchain.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksNode) => { - include_str!("../templates/stacks-node-pod.template.yaml") + StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchain) => { + include_str!("../templates/pods/stacks-blockchain.template.yaml") } - StacksDevnetResource::Service(StacksDevnetService::StacksNode) => { - include_str!("../templates/stacks-node-service.template.yaml") + StacksDevnetResource::Service(StacksDevnetService::StacksBlockchain) => { + include_str!("../templates/services/stacks-blockchain.template.yaml") } StacksDevnetResource::Namespace => include_str!("../templates/namespace.template.yaml"), } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index deac41d..595b9b1 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -117,7 +117,7 @@ enum TestBody { #[test_case("/api/v1/networks", Method::POST, Some(TestBody::CreateNetwork), true => using assert_cannot_create_devnet_err; "409 for create network POST request if devnet exists")] #[test_case("/api/v1/network/{namespace}", Method::GET, None, true => using assert_get_network; "200 for network GET request to existing network")] #[test_case("/api/v1/network/{namespace}", Method::HEAD, None, true => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network HEAD request to existing network")] -#[test_case("/api/v1/network/{namespace}/stacks-node/v2/info/", Method::GET, None, true => using assert_failed_proxy; "proxies requests to downstream nodes")] +#[test_case("/api/v1/network/{namespace}/stacks-blockchain/v2/info/", Method::GET, None, true => using assert_failed_proxy; "proxies requests to downstream nodes")] #[serial_test::serial] #[tokio::test] #[cfg_attr(not(feature = "k8s_tests"), ignore)] @@ -186,7 +186,7 @@ async fn it_responds_to_valid_requests_with_deploy( #[test_case("/api/v1/network/{namespace}", Method::DELETE, true => using assert_cannot_delete_devnet_err; "409 for network DELETE request to non-existing network")] #[test_case("/api/v1/network/{namespace}", Method::GET, true => using assert_not_all_assets_exist_err; "404 for network GET request to non-existing network")] #[test_case("/api/v1/network/{namespace}", Method::HEAD, true => is equal_to (StatusCode::NOT_FOUND, "not found".to_string()); "404 for network HEAD request to non-existing network")] -#[test_case("/api/v1/network/{namespace}/stacks-node/v2/info/", Method::GET, true => using assert_not_all_assets_exist_err; "404 for proxy requests to downstream nodes of non-existing network")] +#[test_case("/api/v1/network/{namespace}/stacks-blockchain/v2/info/", Method::GET, true => using assert_not_all_assets_exist_err; "404 for proxy requests to downstream nodes of non-existing network")] #[tokio::test] #[cfg_attr(not(feature = "k8s_tests"), ignore)] async fn it_responds_to_valid_requests( @@ -378,7 +378,7 @@ fn request_paths_are_parsed_correctly(path: &str) -> PathParts { #[tokio::test] async fn request_mutation_should_create_valid_proxy_destination() { - let path = "/api/v1/some-route/some-network/stacks-node/the//remaining///path"; + let path = "/api/v1/some-route/some-network/stacks-blockchain/the//remaining///path"; let path_parts = get_standardized_path_parts(path); let network = path_parts.network.unwrap(); let subroute = path_parts.subroute.unwrap(); @@ -396,9 +396,9 @@ async fn request_mutation_should_create_valid_proxy_destination() { let actual_url = request.uri().to_string(); let expected = format!( "http://{}.{}.svc.cluster.local:{}/{}", - StacksDevnetService::StacksNode, + StacksDevnetService::StacksBlockchain, network, - get_service_port(StacksDevnetService::StacksNode, ServicePort::RPC).unwrap(), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(), &remainder ); assert_eq!(actual_url, expected); diff --git a/templates/chain-coord-project-manifest-configmap.template.yaml b/templates/chain-coord-project-manifest-configmap.template.yaml deleted file mode 100644 index 1a70f6b..0000000 --- a/templates/chain-coord-project-manifest-configmap.template.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -data: - key: "{value}" -kind: ConfigMap -metadata: - name: project-manifest-conf - namespace: "{namespace}" diff --git a/templates/ci/stacks-devnet-api.template.yaml b/templates/ci/stacks-devnet-api.template.yaml index 1de5aab..3948dda 100644 --- a/templates/ci/stacks-devnet-api.template.yaml +++ b/templates/ci/stacks-devnet-api.template.yaml @@ -1,14 +1,14 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api namespace: devnet --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] @@ -21,14 +21,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api subjects: - kind: ServiceAccount - name: stacks-devnet-api-service-account + name: stacks-devnet-api namespace: devnet roleRef: kind: ClusterRole - name: stacks-devnet-api-service-account + name: stacks-devnet-api apiGroup: rbac.authorization.k8s.io --- @@ -41,10 +41,10 @@ metadata: name: stacks-devnet-api namespace: devnet spec: - serviceAccountName: stacks-devnet-api-service-account + serviceAccountName: stacks-devnet-api containers: - command: ["stacks-devnet-api"] - name: stacks-devnet-api-container + name: stacks-devnet-api image: hirosystems/stacks-devnet-api:ci imagePullPolicy: Never ports: @@ -57,13 +57,13 @@ spec: volumes: - name: config-volume configMap: - name: stacks-devnet-api-conf + name: stacks-devnet-api --- apiVersion: v1 kind: Service metadata: - name: stacks-devnet-api-service + name: stacks-devnet-api namespace: devnet spec: ports: diff --git a/templates/bitcoind-configmap.template.yaml b/templates/configmaps/bitcoind.template.yaml similarity index 81% rename from templates/bitcoind-configmap.template.yaml rename to templates/configmaps/bitcoind.template.yaml index e3a0b84..55fce72 100644 --- a/templates/bitcoind-configmap.template.yaml +++ b/templates/configmaps/bitcoind.template.yaml @@ -3,5 +3,5 @@ data: bitcoin.conf: "{data}" kind: ConfigMap metadata: - name: bitcoind-conf + name: bitcoind namespace: "{namespace}" diff --git a/templates/stacks-api-configmap.template.yaml b/templates/configmaps/chain-coord-deployment-plan.template.yaml similarity index 79% rename from templates/stacks-api-configmap.template.yaml rename to templates/configmaps/chain-coord-deployment-plan.template.yaml index 12d6395..d505124 100644 --- a/templates/stacks-api-configmap.template.yaml +++ b/templates/configmaps/chain-coord-deployment-plan.template.yaml @@ -3,5 +3,5 @@ data: key: "{value}" kind: ConfigMap metadata: - name: stacks-api-conf + name: deployment-plan namespace: "{namespace}" diff --git a/templates/chain-coord-devnet-configmap.template.yaml b/templates/configmaps/chain-coord-devnet.template.yaml similarity index 81% rename from templates/chain-coord-devnet-configmap.template.yaml rename to templates/configmaps/chain-coord-devnet.template.yaml index 0e58907..7397da1 100644 --- a/templates/chain-coord-devnet-configmap.template.yaml +++ b/templates/configmaps/chain-coord-devnet.template.yaml @@ -3,5 +3,5 @@ data: key: "{value}" kind: ConfigMap metadata: - name: devnet-conf + name: devnet namespace: "{namespace}" diff --git a/templates/chain-coord-namespace-configmap.template.yaml b/templates/configmaps/chain-coord-project-dir.template.yaml similarity index 79% rename from templates/chain-coord-namespace-configmap.template.yaml rename to templates/configmaps/chain-coord-project-dir.template.yaml index 15f97bc..c6b8e81 100644 --- a/templates/chain-coord-namespace-configmap.template.yaml +++ b/templates/configmaps/chain-coord-project-dir.template.yaml @@ -3,5 +3,5 @@ data: key: "{value}" kind: ConfigMap metadata: - name: namespace-conf + name: project-dir namespace: "{namespace}" diff --git a/templates/chain-coord-project-dir-configmap.template.yaml b/templates/configmaps/chain-coord-project-manifest.template.yaml similarity index 78% rename from templates/chain-coord-project-dir-configmap.template.yaml rename to templates/configmaps/chain-coord-project-manifest.template.yaml index c62df2c..c657e1e 100644 --- a/templates/chain-coord-project-dir-configmap.template.yaml +++ b/templates/configmaps/chain-coord-project-manifest.template.yaml @@ -3,5 +3,5 @@ data: key: "{value}" kind: ConfigMap metadata: - name: project-dir-conf + name: project-manifest namespace: "{namespace}" diff --git a/templates/configmaps/stacks-blockchain-api-pg.template.yaml b/templates/configmaps/stacks-blockchain-api-pg.template.yaml new file mode 100644 index 0000000..8e15c54 --- /dev/null +++ b/templates/configmaps/stacks-blockchain-api-pg.template.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + key: "{value}" +kind: ConfigMap +metadata: + name: stacks-blockchain-api-pg + namespace: "{namespace}" diff --git a/templates/chain-coord-deployment-plan-configmap.template.yaml b/templates/configmaps/stacks-blockchain-api.template.yaml similarity index 75% rename from templates/chain-coord-deployment-plan-configmap.template.yaml rename to templates/configmaps/stacks-blockchain-api.template.yaml index 6905c73..4027081 100644 --- a/templates/chain-coord-deployment-plan-configmap.template.yaml +++ b/templates/configmaps/stacks-blockchain-api.template.yaml @@ -3,5 +3,5 @@ data: key: "{value}" kind: ConfigMap metadata: - name: deployment-plan-conf + name: stacks-blockchain-api namespace: "{namespace}" diff --git a/templates/stacks-node-configmap.template.yaml b/templates/configmaps/stacks-blockchain.template.yaml similarity index 79% rename from templates/stacks-node-configmap.template.yaml rename to templates/configmaps/stacks-blockchain.template.yaml index 5ca101a..a50cfd5 100644 --- a/templates/stacks-node-configmap.template.yaml +++ b/templates/configmaps/stacks-blockchain.template.yaml @@ -3,5 +3,5 @@ data: Stacks.toml: "{data}" kind: ConfigMap metadata: - name: stacks-node-conf + name: stacks-blockchain namespace: "{namespace}" diff --git a/templates/bitcoind-chain-coordinator-pod.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml similarity index 70% rename from templates/bitcoind-chain-coordinator-pod.template.yaml rename to templates/pods/bitcoind-chain-coordinator.template.yaml index 0d5d020..e7387e3 100644 --- a/templates/bitcoind-chain-coordinator-pod.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -14,7 +14,7 @@ spec: - -pid=/run/bitcoind.pid image: quay.io/hirosystems/bitcoind:devnet-v3 imagePullPolicy: IfNotPresent - name: bitcoind-container + name: bitcoind ports: - containerPort: 18444 name: p2p @@ -24,7 +24,7 @@ spec: protocol: TCP volumeMounts: - mountPath: /etc/bitcoin - name: bitcoind-conf-volume + name: bitcoind readOnly: true - command: - ./stacks-network @@ -36,12 +36,12 @@ spec: env: - name: NAMESPACE valueFrom: - configMapKeyRef: - name: namespace-conf - key: NAMESPACE + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace image: hirosystems/stacks-network-orchestrator:latest imagePullPolicy: Always - name: chain-coordinator-container + name: chain-coordinator ports: - containerPort: 20445 name: coordinator-in @@ -51,26 +51,26 @@ spec: protocol: TCP volumeMounts: - mountPath: /etc/stacks-network/project - name: project-manifest-conf-volume + name: project-manifest - mountPath: /etc/stacks-network/project/settings - name: devnet-conf-volume + name: devnet - mountPath: /etc/stacks-network/project/deployments - name: deployment-plan-conf-volume + name: deployment-plan - mountPath: /etc/stacks-network/project/contracts - name: project-dir-conf-volume + name: project-dir volumes: - configMap: - name: bitcoind-conf - name: bitcoind-conf-volume + name: bitcoind + name: bitcoind - configMap: - name: project-manifest-conf - name: project-manifest-conf-volume + name: project-manifest + name: project-manifest - configMap: - name: devnet-conf - name: devnet-conf-volume + name: devnet + name: devnet - configMap: - name: deployment-plan-conf - name: deployment-plan-conf-volume + name: deployment-plan + name: deployment-plan - configMap: - name: project-dir-conf - name: project-dir-conf-volume + name: project-dir + name: project-dir diff --git a/templates/stacks-api-pod.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml similarity index 68% rename from templates/stacks-api-pod.template.yaml rename to templates/pods/stacks-blockchain-api.template.yaml index 3d27862..2b68789 100644 --- a/templates/stacks-api-pod.template.yaml +++ b/templates/pods/stacks-blockchain-api.template.yaml @@ -2,15 +2,15 @@ apiVersion: v1 kind: Pod metadata: labels: - name: stacks-api - name: stacks-api + name: stacks-blockchain-api + name: stacks-blockchain-api namespace: "{namespace}" spec: containers: - - name: stacks-api-container + - name: stack-blockchains-api envFrom: - configMapRef: - name: stacks-api-conf + name: stacks-blockchain-api optional: false image: hirosystems/stacks-blockchain-api imagePullPolicy: Never @@ -21,10 +21,10 @@ spec: - containerPort: 3700 name: eventport protocol: TCP - - name: stacks-api-postgres + - name: postgres envFrom: - configMapRef: - name: stacks-api-postgres-conf + name: stacks-blockchain-api-pg optional: false image: postgres:14 imagePullPolicy: IfNotPresent @@ -34,8 +34,8 @@ spec: protocol: TCP volumeMounts: - mountPath: /var/lib/postgresql/data - name: stacks-api-conf-volume + name: stacks-blockchain-api-pg volumes: - - name: stacks-api-conf-volume + - name: stacks-blockchain-api-pg persistentVolumeClaim: - claimName: stacks-api-pvc \ No newline at end of file + claimName: stacks-blockchain-api-pg diff --git a/templates/stacks-node-pod.template.yaml b/templates/pods/stacks-blockchain.template.yaml similarity index 69% rename from templates/stacks-node-pod.template.yaml rename to templates/pods/stacks-blockchain.template.yaml index e49706f..421c4a2 100644 --- a/templates/stacks-node-pod.template.yaml +++ b/templates/pods/stacks-blockchain.template.yaml @@ -2,15 +2,15 @@ apiVersion: v1 kind: Pod metadata: labels: - name: stacks-node - name: stacks-node + name: stacks-blockchain + name: stacks-blockchain namespace: "{namespace}" spec: containers: - command: - stacks-node - start - - --config=/src/stacks-node/Stacks.toml + - --config=/src/stacks-blockchain/Stacks.toml env: - name: STACKS_LOG_PP value: "1" @@ -20,7 +20,7 @@ spec: value: "0" image: quay.io/hirosystems/stacks-node:devnet-v3 imagePullPolicy: IfNotPresent - name: stacks-node-container + name: stacks-blockchain ports: - containerPort: 20444 name: p2p @@ -29,10 +29,10 @@ spec: name: rpc protocol: TCP volumeMounts: - - mountPath: /src/stacks-node - name: stacks-node-conf-volume + - mountPath: /src/stacks-blockchain + name: stacks-blockchain readOnly: true volumes: - configMap: - name: stacks-node-conf - name: stacks-node-conf-volume \ No newline at end of file + name: stacks-blockchain + name: stacks-blockchain \ No newline at end of file diff --git a/templates/stacks-api-pvc.template.yaml b/templates/pvcs/stacks-blockchain-api-pg.template.yaml similarity index 88% rename from templates/stacks-api-pvc.template.yaml rename to templates/pvcs/stacks-blockchain-api-pg.template.yaml index 12f1cad..97df3f3 100644 --- a/templates/stacks-api-pvc.template.yaml +++ b/templates/pvcs/stacks-blockchain-api-pg.template.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: stacks-api-pvc + name: stacks-blockchain-api-pg namespace: "{namespace}" spec: accessModes: diff --git a/templates/bitcoind-chain-coordinator-service.template.yaml b/templates/services/bitcoind-chain-coordinator.template.yaml similarity index 90% rename from templates/bitcoind-chain-coordinator-service.template.yaml rename to templates/services/bitcoind-chain-coordinator.template.yaml index 961474d..9c782e0 100644 --- a/templates/bitcoind-chain-coordinator-service.template.yaml +++ b/templates/services/bitcoind-chain-coordinator.template.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: bitcoind-chain-coordinator-service + name: bitcoind-chain-coordinator namespace: "{namespace}" spec: ports: diff --git a/templates/stacks-api-service.template.yaml b/templates/services/stacks-blockchain-api.template.yaml similarity index 83% rename from templates/stacks-api-service.template.yaml rename to templates/services/stacks-blockchain-api.template.yaml index 3a23341..ea5a95b 100644 --- a/templates/stacks-api-service.template.yaml +++ b/templates/services/stacks-blockchain-api.template.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: stacks-api-service + name: stacks-blockchain-api namespace: "{namespace}" spec: ports: @@ -18,4 +18,4 @@ spec: protocol: TCP targetPort: 3700 selector: - name: stacks-api + name: stacks-blockchain-api diff --git a/templates/stacks-node-service.template.yaml b/templates/services/stacks-blockchain.template.yaml similarity index 77% rename from templates/stacks-node-service.template.yaml rename to templates/services/stacks-blockchain.template.yaml index 190b98c..0daa77e 100644 --- a/templates/stacks-node-service.template.yaml +++ b/templates/services/stacks-blockchain.template.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Service metadata: - name: stacks-node-service + name: stacks-blockchain namespace: "{namespace}" spec: ports: @@ -12,4 +12,4 @@ spec: port: 20443 protocol: TCP selector: - name: stacks-node + name: stacks-blockchain diff --git a/templates/stacks-api-postgres-configmap.template.yaml b/templates/stacks-api-postgres-configmap.template.yaml deleted file mode 100644 index 319203c..0000000 --- a/templates/stacks-api-postgres-configmap.template.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -data: - key: "{value}" -kind: ConfigMap -metadata: - name: stacks-api-postgres-conf - namespace: "{namespace}" diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index 62299f2..423efef 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -1,14 +1,14 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api namespace: devnet --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] @@ -22,14 +22,14 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: stacks-devnet-api-service-account + name: stacks-devnet-api subjects: - kind: ServiceAccount - name: stacks-devnet-api-service-account + name: stacks-devnet-api namespace: devnet roleRef: kind: ClusterRole - name: stacks-devnet-api-service-account + name: stacks-devnet-api apiGroup: rbac.authorization.k8s.io --- @@ -42,10 +42,10 @@ metadata: name: stacks-devnet-api namespace: devnet spec: - serviceAccountName: stacks-devnet-api-service-account + serviceAccountName: stacks-devnet-api containers: - command: ["stacks-devnet-api"] - name: stacks-devnet-api-container + name: stacks-devnet-api image: hirosystems/stacks-devnet-api:latest imagePullPolicy: Always ports: @@ -58,13 +58,13 @@ spec: volumes: - name: config-volume configMap: - name: stacks-devnet-api-conf + name: stacks-devnet-api --- apiVersion: v1 kind: Service metadata: - name: stacks-devnet-api-service + name: stacks-devnet-api namespace: devnet spec: ports: From f7e6bc3b4708b764a8aa3ea0f9f255577686cc86 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 25 Sep 2023 16:16:59 -0400 Subject: [PATCH 16/35] feat: allow configuring k8s context with env var (#71) --- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 2 +- src/tests/mod.rs | 4 ++-- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b710789..35d9791 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,8 @@ use k8s_openapi::{ }; use kube::{ api::{Api, DeleteParams, PostParams}, - Client, + config::KubeConfigOptions, + Client, Config, }; use resources::{ pvc::StacksDevnetPvc, @@ -17,8 +18,8 @@ use resources::{ StacksDevnetResource, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::thread::sleep; use std::{collections::BTreeMap, str::FromStr, time::Duration}; +use std::{env, thread::sleep}; use strum::IntoEnumIterator; use tower::BoxError; @@ -99,17 +100,55 @@ pub struct StacksDevnetApiK8sManager { } impl StacksDevnetApiK8sManager { - pub async fn default(ctx: &Context) -> StacksDevnetApiK8sManager { - let client = Client::try_default() - .await - .expect("could not create kube client"); + pub async fn new(ctx: &Context) -> StacksDevnetApiK8sManager { + let context = match env::var("KUBE_CONTEXT") { + Ok(context) => Some(context), + Err(_) => { + if cfg!(test) { + let is_ci = match env::var("GITHUB_ACTIONS") { + Ok(is_ci) => is_ci == format!("true"), + Err(_) => false, + }; + if is_ci { + None + } else { + // ensures that if no context is supplied and we're running + // tests locally, we deploy to the local kind cluster + Some(format!("kind-kind")) + } + } else { + None + } + } + }; + let client = match context { + Some(context) => { + let kube_config = KubeConfigOptions { + context: Some(context.clone()), + cluster: Some(context), + user: None, + }; + let client_config = + Config::from_kubeconfig(&kube_config) + .await + .unwrap_or_else(|e| { + panic!("could not create kube client config: {}", e.to_string()) + }); + Client::try_from(client_config) + .unwrap_or_else(|e| panic!("could not create kube client: {}", e.to_string())) + } + None => Client::try_default() + .await + .expect("could not create kube client"), + }; + StacksDevnetApiK8sManager { client, ctx: ctx.to_owned(), } } - pub async fn new( + pub async fn from_service( service: S, default_namespace: T, ctx: &Context, diff --git a/src/main.rs b/src/main.rs index 3ed9209..5974cc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ async fn main() { logger: Some(logger), tracer: false, }; - let k8s_manager = StacksDevnetApiK8sManager::default(&ctx).await; + let k8s_manager = StacksDevnetApiK8sManager::new(&ctx).await; let config_path = match env::var("CONFIG_PATH") { Ok(path) => path, Err(_) => { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 595b9b1..e4f8b3f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -54,7 +54,7 @@ async fn get_k8s_manager() -> (StacksDevnetApiK8sManager, Context) { let logger = hiro_system_kit::log::setup_logger(); let _guard = hiro_system_kit::log::setup_global_logger(logger.clone()); let ctx = Context::empty(); - let k8s_manager = StacksDevnetApiK8sManager::default(&ctx).await; + let k8s_manager = StacksDevnetApiK8sManager::new(&ctx).await; (k8s_manager, ctx) } @@ -269,7 +269,7 @@ async fn get_mock_k8s_manager() -> (StacksDevnetApiK8sManager, Context) { let logger = hiro_system_kit::log::setup_logger(); let _guard = hiro_system_kit::log::setup_global_logger(logger.clone()); let ctx = Context::empty(); - let k8s_manager = StacksDevnetApiK8sManager::new(mock_service, "default", &ctx).await; + let k8s_manager = StacksDevnetApiK8sManager::from_service(mock_service, "default", &ctx).await; (k8s_manager, ctx) } From 0b959c2df4ecbe52a4379112eed137031365c251 Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Mon, 25 Sep 2023 16:37:26 -0400 Subject: [PATCH 17/35] fix: set img pull policy for stacks blockchain api --- templates/pods/stacks-blockchain-api.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml index 2b68789..16b7b54 100644 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ b/templates/pods/stacks-blockchain-api.template.yaml @@ -13,7 +13,7 @@ spec: name: stacks-blockchain-api optional: false image: hirosystems/stacks-blockchain-api - imagePullPolicy: Never + imagePullPolicy: Always ports: - containerPort: 3999 name: api From 84a949472f9e2ac5674833dd2ed7ed55deee8a8a Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Tue, 26 Sep 2023 11:13:15 -0400 Subject: [PATCH 18/35] chore: set `IfNotPresent` img pull policy (#72) --- templates/pods/bitcoind-chain-coordinator.template.yaml | 2 +- templates/pods/stacks-blockchain-api.template.yaml | 2 +- templates/stacks-devnet-api.template.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml index e7387e3..8f2fdd0 100644 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -40,7 +40,7 @@ spec: apiVersion: v1 fieldPath: metadata.namespace image: hirosystems/stacks-network-orchestrator:latest - imagePullPolicy: Always + imagePullPolicy: IfNotPresent name: chain-coordinator ports: - containerPort: 20445 diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml index 16b7b54..01511e5 100644 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ b/templates/pods/stacks-blockchain-api.template.yaml @@ -13,7 +13,7 @@ spec: name: stacks-blockchain-api optional: false image: hirosystems/stacks-blockchain-api - imagePullPolicy: Always + imagePullPolicy: IfNotPresent ports: - containerPort: 3999 name: api diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index 423efef..cf2e91f 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -47,7 +47,7 @@ spec: - command: ["stacks-devnet-api"] name: stacks-devnet-api image: hirosystems/stacks-devnet-api:latest - imagePullPolicy: Always + imagePullPolicy: IfNotPresent ports: - containerPort: 8477 name: api From 781274127c8fb7d6a8ee95d4d92218e650b9c01e Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Tue, 26 Sep 2023 11:18:51 -0400 Subject: [PATCH 19/35] fix: add subPath for postgres volume mount (#73) --- templates/pods/stacks-blockchain-api.template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml index 01511e5..42a20e7 100644 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ b/templates/pods/stacks-blockchain-api.template.yaml @@ -35,6 +35,7 @@ spec: volumeMounts: - mountPath: /var/lib/postgresql/data name: stacks-blockchain-api-pg + subPath: postgres volumes: - name: stacks-blockchain-api-pg persistentVolumeClaim: From 7b52f93e82d46d1bb127dcc8f9c8c095f3426dc2 Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Tue, 26 Sep 2023 12:27:50 -0400 Subject: [PATCH 20/35] fix: api container spelling fix --- scripts/get-logs.sh | 10 +++++----- templates/pods/stacks-blockchain-api.template.yaml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/get-logs.sh b/scripts/get-logs.sh index 05302ec..2405fe3 100755 --- a/scripts/get-logs.sh +++ b/scripts/get-logs.sh @@ -1,5 +1,5 @@ -kubectl --context kind-kind logs stacks-blockchain --namespace $1 > ./logs/stacks-blockchain.txt & \ -kubectl --context kind-kind logs stacks-blockchain-api --namespace $1 -c stacks-blockchain-api > ./logs/stack-blockchains-api.txt & \ -kubectl --context kind-kind logs stacks-blockchain-api --namespace $1 -c postgres > ./logs/stacks-blockchain-api-pg.txt & \ -kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c bitcoind > ./logs/bitcoin-node.txt & \ -kubectl --context kind-kind logs bitcoind-chain-coordinator --namespace $1 -c chain-coordinator > ./logs/chain-coordinator.txt \ No newline at end of file +kubectl logs stacks-blockchain --namespace $1 > ./logs/stacks-blockchain.txt & \ +kubectl logs stacks-blockchain-api --namespace $1 -c stacks-blockchain-api > ./logs/stacks-blockchain-api.txt & \ +kubectl logs stacks-blockchain-api --namespace $1 -c postgres > ./logs/stacks-blockchain-api-pg.txt & \ +kubectl logs bitcoind-chain-coordinator --namespace $1 -c bitcoind > ./logs/bitcoin-node.txt & \ +kubectl logs bitcoind-chain-coordinator --namespace $1 -c chain-coordinator > ./logs/chain-coordinator.txt \ No newline at end of file diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml index 42a20e7..94f58de 100644 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ b/templates/pods/stacks-blockchain-api.template.yaml @@ -7,7 +7,7 @@ metadata: namespace: "{namespace}" spec: containers: - - name: stack-blockchains-api + - name: stacks-blockchain-api envFrom: - configMapRef: name: stacks-blockchain-api From aeb19ef5ee36c32cebbbc8c3fb681f80332c001c Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Tue, 26 Sep 2023 17:12:37 -0400 Subject: [PATCH 21/35] chore: specify hash for stacks-network-orchestrator --- templates/pods/bitcoind-chain-coordinator.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml index 8f2fdd0..ecfbc07 100644 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -39,7 +39,7 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator:latest + image: hirosystems/stacks-network-orchestrator@sha256:a9cfca24df3c9e46419174f190b14620837d4a277b6fdd3ba8495d0e7625dce8 imagePullPolicy: IfNotPresent name: chain-coordinator ports: From 638ba383785137d568db99cf81441bf18296c87e Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Wed, 27 Sep 2023 11:15:31 -0400 Subject: [PATCH 22/35] chore: use stacks-network-orchestrator img with logging --- templates/pods/bitcoind-chain-coordinator.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml index ecfbc07..76aea1a 100644 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -39,7 +39,7 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator@sha256:a9cfca24df3c9e46419174f190b14620837d4a277b6fdd3ba8495d0e7625dce8 + image: hirosystems/stacks-network-orchestrator@sha256:1a1f60688759bc742bd1b64384eedba38faed46e0067cb3c121914004fe1df0a imagePullPolicy: IfNotPresent name: chain-coordinator ports: From 82df960e4a33ca439fd3791a4202f6f9a1434912 Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Wed, 27 Sep 2023 13:05:29 -0400 Subject: [PATCH 23/35] chore: use debug img --- templates/pods/bitcoind-chain-coordinator.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml index 76aea1a..c4aaab4 100644 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -39,7 +39,7 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator@sha256:1a1f60688759bc742bd1b64384eedba38faed46e0067cb3c121914004fe1df0a + image: hirosystems/stacks-network-orchestrator:rpc-error imagePullPolicy: IfNotPresent name: chain-coordinator ports: From 64c6b6878a6082636cd5026ee863fa8bf52bda9b Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Wed, 27 Sep 2023 13:53:55 -0400 Subject: [PATCH 24/35] chore(debug): set Always img pull policy --- templates/pods/bitcoind-chain-coordinator.template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml index c4aaab4..4db1864 100644 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ b/templates/pods/bitcoind-chain-coordinator.template.yaml @@ -40,7 +40,7 @@ spec: apiVersion: v1 fieldPath: metadata.namespace image: hirosystems/stacks-network-orchestrator:rpc-error - imagePullPolicy: IfNotPresent + imagePullPolicy: Always name: chain-coordinator ports: - containerPort: 20445 From 5d8c5b1615022db32549126e13a7b612a86b0513 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Thu, 28 Sep 2023 21:04:14 -0400 Subject: [PATCH 25/35] docs: update readme (#74) --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9662b5..41a16cf 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,17 @@ You should now be ready to deploy this service to your local Kubernetes cluster! The `Config.toml` at the root directory of the project can be used to control some settings. This same file can be used to update both the stable and development build. The following settings are supported: - `allowed_origins` - this setting is an array of strings and is used to set what origins are allowed in cross-origin requests. For example, `allowed_origins = ["*"]` allows any origins to make requests to this service, while `allowed_origins = ["localhost:3002", "dev.platform.so"]` will only allow requests from the two specified hosts. - `allowed_methods` - this setting is an array of strings that sets what HTTP methods can be made to this server. + - `auth_header` - all requests to the API specify a network id which indicates the network that is being modified. An auth header is checked on all requests to ensure that the value of the auth header matches the id of the request. This configuration value dictates the name of that auth header + - `namespace_prefix` - the user's id that is used for the auth header differs slightly from the network id that is used to differentiate devnets. This value is used to determine how to mutate a user id to create a namespace. For example, if the namespace prefix is `zzz-platform`, and a user makes a requests with an auth header value of `auth0|test-namespace`, the devnet API will ensure that the request is trying to create or update a devnet with namespace `zzz-platform-auth0-test-namespace`. + +## Environment Variables +The following environment variables can be provided at runtime to further configure the API: + - `KUBE_CONTEXT="" stacks-devnet-api` + - With this flag, you can specify the context that will be used for all requests. + - Default: the devnet API will deploy assets to the user's default Kubernetes context. Run `kubectl config get-context` to see your default. + - `CONFIG_PATH="/path/to/config" stacks-devnet-api` + - Use this flag to specify the path of the `Config.toml` + - Default: in development mode (building the app using `cargo build`), the default location is `./Config.toml`; in release mode (building the app using `cargo build --release`), the default location is `/etc/config/Config.toml` ## Deploying the Stable Version @@ -115,4 +126,4 @@ Join our community and stay connected with the latest updates and discussions: - [Visit hiro.so](https://www.hiro.so/) for updates and subcribing to the mailing list. -- Follow [Hiro on Twitter.](https://twitter.com/hirosystems) \ No newline at end of file +- Follow [Hiro on Twitter.](https://twitter.com/hirosystems) From 6399c19231ee12f7f7bdb3ed2fa90ece3c0e4000 Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 9 Oct 2023 09:40:59 -0400 Subject: [PATCH 26/35] feat: remove pods; implement deployments/stateful sets (#75) --- src/config.rs | 2 + src/lib.rs | 492 +++++++++++++++--- src/main.rs | 12 +- src/resources/deployment.rs | 17 + src/resources/mod.rs | 8 +- src/resources/pvc.rs | 2 +- src/resources/stateful_set.rs | 15 + src/resources/tests.rs | 15 +- src/routes.rs | 12 +- src/template_parser.rs | 20 +- src/tests/mod.rs | 3 +- templates/ci/stacks-devnet-api.template.yaml | 5 +- .../bitcoind-chain-coordinator.template.yaml | 113 ++++ .../stacks-blockchain.template.yaml | 69 +++ .../bitcoind-chain-coordinator.template.yaml | 76 --- .../pods/stacks-blockchain-api.template.yaml | 42 -- .../pods/stacks-blockchain.template.yaml | 38 -- .../stacks-blockchain-api-pg.template.yaml | 15 - .../bitcoind-chain-coordinator.template.yaml | 18 +- .../stacks-blockchain-api.template.yaml | 16 +- .../services/stacks-blockchain.template.yaml | 14 +- templates/stacks-devnet-api.template.yaml | 5 +- .../stacks-blockchain-api.template.yaml | 86 +++ 23 files changed, 813 insertions(+), 282 deletions(-) create mode 100644 src/resources/deployment.rs create mode 100644 src/resources/stateful_set.rs create mode 100644 templates/deployments/bitcoind-chain-coordinator.template.yaml create mode 100644 templates/deployments/stacks-blockchain.template.yaml delete mode 100644 templates/pods/bitcoind-chain-coordinator.template.yaml delete mode 100644 templates/pods/stacks-blockchain-api.template.yaml delete mode 100644 templates/pods/stacks-blockchain.template.yaml delete mode 100644 templates/pvcs/stacks-blockchain-api-pg.template.yaml create mode 100644 templates/stateful-sets/stacks-blockchain-api.template.yaml diff --git a/src/config.rs b/src/config.rs index 465b799..efea780 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ const CONTRACT_DIR: &str = "/etc/stacks-network/project/contracts"; #[derive(Serialize, Deserialize, Debug)] pub struct ValidatedStacksDevnetConfig { pub namespace: String, + pub user_id: String, pub devnet_config: DevnetConfig, pub accounts: BTreeMap, pub project_manifest_yaml_string: String, @@ -72,6 +73,7 @@ impl StacksDevnetConfig { Ok(ValidatedStacksDevnetConfig { namespace: self.namespace, + user_id: user_id.to_owned(), devnet_config: devnet_config.to_owned(), accounts: self.network_manifest.accounts, project_manifest_yaml_string: project_manifest_yaml_string.to_owned(), diff --git a/src/lib.rs b/src/lib.rs index 35d9791..400de22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,17 +4,22 @@ use futures::future::try_join4; use hiro_system_kit::{slog, Logger}; use hyper::{body::Bytes, Body, Client as HttpClient, Request, Response, Uri}; use k8s_openapi::{ - api::core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Service}, + api::{ + apps::v1::{Deployment, StatefulSet}, + core::v1::{ConfigMap, Namespace, PersistentVolumeClaim, Pod, Service}, + }, NamespaceResourceScope, }; use kube::{ - api::{Api, DeleteParams, PostParams}, + api::{Api, DeleteParams, ListParams, PostParams}, config::KubeConfigOptions, Client, Config, }; use resources::{ + deployment::StacksDevnetDeployment, pvc::StacksDevnetPvc, service::{get_service_port, ServicePort}, + stateful_set::StacksDevnetStatefulSet, StacksDevnetResource, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -37,6 +42,10 @@ use crate::resources::configmap::StacksDevnetConfigmap; use crate::resources::pod::StacksDevnetPod; use crate::resources::service::{get_service_url, StacksDevnetService}; +const COMPONENT_SELECTOR: &str = "app.kubernetes.io/component"; +const USER_SELECTOR: &str = "app.kubernetes.io/instance"; +const NAME_SELECTOR: &str = "app.kubernetes.io/name"; + #[derive(Clone, Debug)] pub struct DevNetError { pub message: String, @@ -173,6 +182,7 @@ impl StacksDevnetApiK8sManager { config: ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let context = format!("NAMESPACE: {}", &namespace); let namespace_exists = self.check_namespace_exists(&namespace).await?; @@ -192,7 +202,9 @@ impl StacksDevnetApiK8sManager { } } - let any_assets_exist = self.check_any_devnet_assets_exist(&namespace).await?; + let any_assets_exist = self + .check_any_devnet_assets_exist(&namespace, &user_id) + .await?; if any_assets_exist { let msg = format!( "cannot create devnet because assets already exist {}", @@ -205,25 +217,45 @@ impl StacksDevnetApiK8sManager { }); }; - self.deploy_bitcoin_node_pod(&config).await?; + self.deploy_bitcoin_node(&config).await?; sleep(Duration::from_secs(5)); - self.deploy_stacks_node_pod(&config).await?; + self.deploy_stacks_blockchain(&config).await?; if !config.disable_stacks_api { - self.deploy_stacks_api_pod(&namespace).await?; + self.deploy_stacks_blockchain_api(&config).await?; } Ok(()) } - pub async fn delete_devnet(&self, namespace: &str) -> Result<(), DevNetError> { - match self.check_any_devnet_assets_exist(&namespace).await? { + pub async fn delete_devnet(&self, namespace: &str, user_id: &str) -> Result<(), DevNetError> { + match self + .check_any_devnet_assets_exist(namespace, user_id) + .await? + { true => { let mut errors = vec![]; - let pods: Vec = StacksDevnetPod::iter().map(|p| p.to_string()).collect(); - for pod in pods { - if let Err(e) = self.delete_resource::(namespace, &pod).await { + let deployments: Vec = StacksDevnetDeployment::iter() + .map(|p| p.to_string()) + .collect(); + for deployment in deployments { + if let Err(e) = self + .delete_resource::(namespace, &deployment) + .await + { + errors.push(e); + } + } + + let stateful_sets: Vec = StacksDevnetStatefulSet::iter() + .map(|p| p.to_string()) + .collect(); + for stateful_set in stateful_sets { + if let Err(e) = self + .delete_resource::(namespace, &stateful_set) + .await + { errors.push(e); } } @@ -248,15 +280,17 @@ impl StacksDevnetApiK8sManager { } } - let pvcs: Vec = StacksDevnetPvc::iter().map(|s| s.to_string()).collect(); + let pvcs: Vec = + StacksDevnetPvc::iter().map(|pvc| pvc.to_string()).collect(); for pvc in pvcs { if let Err(e) = self - .delete_resource::(namespace, &pvc) + .delete_resource_by_label::(namespace, &pvc, user_id) .await { errors.push(e); } } + if errors.is_empty() { Ok(()) } else if errors.len() == 1 { @@ -333,6 +367,7 @@ impl StacksDevnetApiK8sManager { pub async fn check_any_devnet_assets_exist( &self, namespace: &str, + user_id: &str, ) -> Result { self.ctx.try_log(|logger| { slog::info!( @@ -341,9 +376,27 @@ impl StacksDevnetApiK8sManager { &namespace ) }); + for deployment in StacksDevnetDeployment::iter() { + if self + .check_resource_exists::(namespace, &deployment.to_string()) + .await? + { + return Ok(true); + } + } + + for stateful_set in StacksDevnetStatefulSet::iter() { + if self + .check_resource_exists::(namespace, &stateful_set.to_string()) + .await? + { + return Ok(true); + } + } + for pod in StacksDevnetPod::iter() { if self - .check_resource_exists::(namespace, &pod.to_string()) + .check_resource_exists_by_label::(namespace, &pod.to_string(), user_id) .await? { return Ok(true); @@ -370,13 +423,16 @@ impl StacksDevnetApiK8sManager { for pvc in StacksDevnetPvc::iter() { if self - .check_resource_exists::(namespace, &pvc.to_string()) + .check_resource_exists_by_label::( + namespace, + &pvc.to_string(), + user_id, + ) .await? { return Ok(true); } } - Ok(false) } @@ -391,36 +447,36 @@ impl StacksDevnetApiK8sManager { &namespace ) }); - for pod in StacksDevnetPod::iter() { + for deployment in StacksDevnetDeployment::iter() { if !self - .check_resource_exists::(namespace, &pod.to_string()) + .check_resource_exists::(namespace, &deployment.to_string()) .await? { return Ok(false); } } - for configmap in StacksDevnetConfigmap::iter() { + for stateful_set in StacksDevnetStatefulSet::iter() { if !self - .check_resource_exists::(namespace, &configmap.to_string()) + .check_resource_exists::(namespace, &stateful_set.to_string()) .await? { return Ok(false); } } - for service in StacksDevnetService::iter() { + for configmap in StacksDevnetConfigmap::iter() { if !self - .check_resource_exists::(namespace, &service.to_string()) + .check_resource_exists::(namespace, &configmap.to_string()) .await? { return Ok(false); } } - for pvc in StacksDevnetPvc::iter() { + for service in StacksDevnetService::iter() { if !self - .check_resource_exists::(namespace, &pvc.to_string()) + .check_resource_exists::(namespace, &service.to_string()) .await? { return Ok(false); @@ -433,6 +489,7 @@ impl StacksDevnetApiK8sManager { async fn get_pod_status_info( &self, namespace: &str, + user_id: &str, pod: StacksDevnetPod, ) -> Result { let context = format!("NAMESPACE: {}, POD: {}", namespace, pod); @@ -441,24 +498,38 @@ impl StacksDevnetApiK8sManager { slog::info!(logger, "getting pod status {}", context) }); let pod_api: Api = Api::namespaced(self.client.to_owned(), &namespace); - let pod_name = pod.to_string(); - match pod_api.get_status(&pod_name).await { - Ok(pod_with_status) => match pod_with_status.status { - Some(status) => { - self.ctx.try_log(|logger: &hiro_system_kit::Logger| { - slog::info!(logger, "successfully retrieved pod status {}", context) - }); - let start_time = match status.start_time { - Some(st) => Some(st.0.to_string()), - None => None, - }; - Ok(PodStatusResponse { - status: status.phase, - start_time, - }) + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={pod}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={pod}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + match pod_api.list(&lp).await { + Ok(pods) => { + let pod_with_status = &pods.items[0]; + match &pod_with_status.status { + Some(status) => { + self.ctx.try_log(|logger: &hiro_system_kit::Logger| { + slog::info!(logger, "successfully retrieved pod status {}", context) + }); + let start_time = match &status.start_time { + Some(st) => Some(st.0.to_string()), + None => None, + }; + Ok(PodStatusResponse { + status: status.phase.to_owned(), + start_time, + }) + } + None => Ok(PodStatusResponse::default()), } - None => Ok(PodStatusResponse::default()), - }, + } Err(e) => { let (msg, code) = match e { kube::Error::Api(api_error) => (api_error.message, api_error.code), @@ -554,6 +625,7 @@ impl StacksDevnetApiK8sManager { pub async fn get_devnet_info( &self, namespace: &str, + user_id: &str, ) -> Result { let context = format!("NAMESPACE: {}", namespace); @@ -587,9 +659,17 @@ impl StacksDevnetApiK8sManager { }, chain_info, ) = try_join4( - self.get_pod_status_info(&namespace, StacksDevnetPod::BitcoindNode), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchain), - self.get_pod_status_info(&namespace, StacksDevnetPod::StacksBlockchainApi), + self.get_pod_status_info(&namespace, user_id, StacksDevnetPod::BitcoindNode), + self.get_pod_status_info( + &namespace, + user_id, + StacksDevnetPod::StacksBlockchain, + ), + self.get_pod_status_info( + &namespace, + user_id, + StacksDevnetPod::StacksBlockchainApi, + ), self.get_stacks_v2_info(&namespace), ) .await?; @@ -644,6 +724,69 @@ impl StacksDevnetApiK8sManager { } } + async fn get_resource_by_label>( + &self, + namespace: &str, + name: &str, + user_id: &str, + ) -> Result, DevNetError> + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + K: Serialize, + { + let resource_api: Api = Api::namespaced(self.client.to_owned(), &namespace); + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={name}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={name}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + let resource_details = format!( + "RESOURCE: {}, NAME: {}, NAMESPACE: {}", + std::any::type_name::(), + name, + namespace + ); + self.ctx + .try_log(|logger| slog::info!(logger, "fetching {}", resource_details)); + + match resource_api.list(&lp).await { + Ok(pods) => { + if pods.items.len() > 0 { + let pod = &pods.items[0]; + Ok(Some(pod.clone())) + } else { + Ok(None) + } + } + Err(e) => { + let (msg, code) = match e { + kube::Error::Api(api_error) => { + if api_error.code == 404 { + return Ok(None); + } + (api_error.message, api_error.code) + } + e => (e.to_string(), 500), + }; + let msg = format!("failed to fetch {}, ERROR: {}", resource_details, msg); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + Err(DevNetError { + message: msg, + code: code, + }) + } + } + } + async fn get_resource>( &self, namespace: &str, @@ -697,6 +840,28 @@ impl StacksDevnetApiK8sManager { } } + async fn check_resource_exists_by_label>( + &self, + namespace: &str, + name: &str, + user_id: &str, + ) -> Result + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + K: Serialize, + { + match self + .get_resource_by_label::(namespace, name, user_id) + .await? + { + Some(_) => Ok(true), + None => Ok(false), + } + } + async fn check_resource_exists>( &self, namespace: &str, @@ -772,21 +937,140 @@ impl StacksDevnetApiK8sManager { } } - async fn deploy_pod(&self, pod: StacksDevnetPod, namespace: &str) -> Result<(), DevNetError> { - let mut pod: Pod = self.get_resource_from_file(StacksDevnetResource::Pod(pod))?; + async fn deploy_deployment( + &self, + deployment: StacksDevnetDeployment, + namespace: &str, + user_id: &str, + ) -> Result<(), DevNetError> { + let mut deployment: Deployment = + self.get_resource_from_file(StacksDevnetResource::Deployment(deployment))?; + + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); - pod.metadata.namespace = Some(namespace.to_owned()); - self.deploy_resource(namespace, pod, "pod").await + if let Some(mut labels) = deployment.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + deployment.metadata.labels = Some(labels); + } + + if let Some(mut spec) = deployment.clone().spec { + if let Some(mut match_labels) = spec.selector.match_labels { + if let Some(match_label) = match_labels.get_mut(&key) { + *match_label = user_id.clone(); + } else { + match_labels.insert(key.clone(), user_id.clone()); + } + spec.selector.match_labels = Some(match_labels); + } + + if let Some(mut metadata) = spec.template.metadata { + if let Some(mut labels) = metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + metadata.labels = Some(labels); + } + spec.template.metadata = Some(metadata); + } + + deployment.spec = Some(spec); + } + + deployment.metadata.namespace = Some(namespace.to_owned()); + self.deploy_resource(namespace, deployment, "deployment") + .await + } + + async fn deploy_stateful_set( + &self, + stateful_set: StacksDevnetStatefulSet, + namespace: &str, + user_id: &str, + ) -> Result<(), DevNetError> { + let mut stateful_set: StatefulSet = + self.get_resource_from_file(StacksDevnetResource::StatefulSet(stateful_set))?; + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); + + if let Some(mut labels) = stateful_set.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + stateful_set.metadata.labels = Some(labels); + } + + if let Some(mut spec) = stateful_set.clone().spec { + if let Some(mut match_labels) = spec.selector.match_labels { + if let Some(match_label) = match_labels.get_mut(&key) { + *match_label = user_id.clone(); + } else { + match_labels.insert(key.clone(), user_id.clone()); + } + spec.selector.match_labels = Some(match_labels); + } + + if let Some(mut metadata) = spec.template.metadata { + if let Some(mut labels) = metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + metadata.labels = Some(labels); + } + spec.template.metadata = Some(metadata); + } + + stateful_set.spec = Some(spec); + } + + stateful_set.metadata.namespace = Some(namespace.to_owned()); + self.deploy_resource(namespace, stateful_set, "stateful_set") + .await } async fn deploy_service( &self, service: StacksDevnetService, namespace: &str, + user_id: &str, ) -> Result<(), DevNetError> { let mut service: Service = self.get_resource_from_file(StacksDevnetResource::Service(service))?; + let key = "app.kubernetes.io/instance".to_string(); + let user_id = user_id.to_owned(); + + if let Some(mut labels) = service.clone().metadata.labels { + if let Some(label) = labels.get_mut(&key) { + *label = user_id.clone(); + } else { + labels.insert(key.clone(), user_id.clone()); + } + service.metadata.labels = Some(labels); + } + + if let Some(mut spec) = service.clone().spec { + if let Some(mut selector_map) = spec.selector { + if let Some(selector_entry) = selector_map.get_mut(&key) { + *selector_entry = user_id.clone(); + } else { + selector_map.insert(key.clone(), user_id.clone()); + } + spec.selector = Some(selector_map); + } + + service.spec = Some(spec); + } service.metadata.namespace = Some(namespace.to_owned()); self.deploy_resource(namespace, service, "service").await } @@ -813,20 +1097,12 @@ impl StacksDevnetApiK8sManager { .await } - async fn deploy_pvc(&self, pvc: StacksDevnetPvc, namespace: &str) -> Result<(), DevNetError> { - let mut pvc: PersistentVolumeClaim = - self.get_resource_from_file(StacksDevnetResource::Pvc(pvc))?; - - pvc.metadata.namespace = Some(namespace.to_owned()); - - self.deploy_resource(namespace, pvc, "pvc").await - } - - async fn deploy_bitcoin_node_pod( + async fn deploy_bitcoin_node( &self, config: &ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let devnet_config = &config.devnet_config; let bitcoin_rpc_port = @@ -909,20 +1185,21 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pod(StacksDevnetPod::BitcoindNode, &namespace) + self.deploy_deployment(StacksDevnetDeployment::BitcoindNode, &namespace, &user_id) .await?; - self.deploy_service(StacksDevnetService::BitcoindNode, namespace) + self.deploy_service(StacksDevnetService::BitcoindNode, namespace, &user_id) .await?; Ok(()) } - async fn deploy_stacks_node_pod( + async fn deploy_stacks_blockchain( &self, config: &ValidatedStacksDevnetConfig, ) -> Result<(), DevNetError> { let namespace = &config.namespace; + let user_id = &config.user_id; let devnet_config = &config.devnet_config; let chain_coordinator_ingestion_port = @@ -1086,16 +1363,25 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pod(StacksDevnetPod::StacksBlockchain, &namespace) - .await?; + self.deploy_deployment( + StacksDevnetDeployment::StacksBlockchain, + &namespace, + &user_id, + ) + .await?; - self.deploy_service(StacksDevnetService::StacksBlockchain, namespace) + self.deploy_service(StacksDevnetService::StacksBlockchain, namespace, &user_id) .await?; Ok(()) } - async fn deploy_stacks_api_pod(&self, namespace: &str) -> Result<(), DevNetError> { + async fn deploy_stacks_blockchain_api( + &self, + config: &ValidatedStacksDevnetConfig, + ) -> Result<(), DevNetError> { + let namespace = &config.namespace; + let user_id = &config.user_id; // configmap env vars for pg conatainer let stacks_api_pg_env = Vec::from([ ("POSTGRES_PASSWORD".into(), "postgres".into()), @@ -1144,14 +1430,19 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_pvc(StacksDevnetPvc::StacksBlockchainApiPg, &namespace) - .await?; - - self.deploy_pod(StacksDevnetPod::StacksBlockchainApi, &namespace) - .await?; + self.deploy_stateful_set( + StacksDevnetStatefulSet::StacksBlockchainApi, + &namespace, + user_id, + ) + .await?; - self.deploy_service(StacksDevnetService::StacksBlockchainApi, &namespace) - .await?; + self.deploy_service( + StacksDevnetService::StacksBlockchainApi, + &namespace, + &user_id, + ) + .await?; Ok(()) } @@ -1199,6 +1490,63 @@ impl StacksDevnetApiK8sManager { } } } + + async fn delete_resource_by_label>( + &self, + namespace: &str, + resource_name: &str, + user_id: &str, + ) -> Result<(), DevNetError> + where + ::DynamicType: Default, + K: Clone, + K: DeserializeOwned, + K: std::fmt::Debug, + { + let api: Api = Api::namespaced(self.client.to_owned(), &namespace); + let dp = DeleteParams::default(); + + let pod_label_selector = format!("{COMPONENT_SELECTOR}={resource_name}"); + let user_label_selector = format!("{USER_SELECTOR}={user_id}"); + let name_label_selector = format!("{NAME_SELECTOR}={resource_name}"); + let label_selector = + format!("{pod_label_selector},{user_label_selector},{name_label_selector}"); + + let lp = ListParams::default() + .match_any() + .labels(&label_selector) + .limit(1); + + let resource_details = format!( + "RESOURCE: {}, NAME: {}, NAMESPACE: {}", + std::any::type_name::(), + resource_name, + namespace + ); + self.ctx + .try_log(|logger| slog::info!(logger, "deleting {}", resource_details)); + match api.delete_collection(&dp, &lp).await { + Ok(_) => { + self.ctx.try_log(|logger| { + slog::info!(logger, "successfully deleted {}", resource_details) + }); + Ok(()) + } + Err(e) => { + let e = match e { + kube::Error::Api(api_error) => (api_error.message, api_error.code), + e => (e.to_string(), 500), + }; + let msg = format!("failed to delete {}, ERROR: {}", resource_details, e.0); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + Err(DevNetError { + message: msg, + code: e.1, + }) + } + } + } + pub async fn delete_namespace(&self, namespace_str: &str) -> Result<(), DevNetError> { if cfg!(debug_assertions) { use kube::ResourceExt; diff --git a/src/main.rs b/src/main.rs index 5974cc1..491eebb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,9 +148,15 @@ async fn handle_request( // so it must be a request to DELETE a network or GET network info if path_parts.subroute.is_none() { return match method { - &Method::DELETE => handle_delete_devnet(k8s_manager, &network, responder).await, - &Method::GET => handle_get_devnet(k8s_manager, &network, responder, ctx).await, - &Method::HEAD => handle_check_devnet(k8s_manager, &network, responder).await, + &Method::DELETE => { + handle_delete_devnet(k8s_manager, &network, &user_id, responder).await + } + &Method::GET => { + handle_get_devnet(k8s_manager, &network, &user_id, responder, ctx).await + } + &Method::HEAD => { + handle_check_devnet(k8s_manager, &network, &user_id, responder).await + } _ => responder .err_method_not_allowed("can only GET/DELETE/HEAD at provided route".into()), }; diff --git a/src/resources/deployment.rs b/src/resources/deployment.rs new file mode 100644 index 0000000..8635cd6 --- /dev/null +++ b/src/resources/deployment.rs @@ -0,0 +1,17 @@ +use std::fmt; +use strum_macros::EnumIter; + +#[derive(EnumIter, Debug)] +pub enum StacksDevnetDeployment { + BitcoindNode, + StacksBlockchain, +} + +impl fmt::Display for StacksDevnetDeployment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + StacksDevnetDeployment::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), + StacksDevnetDeployment::StacksBlockchain => write!(f, "stacks-blockchain"), + } + } +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 918143b..ff8041d 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -1,18 +1,22 @@ use self::{ - configmap::StacksDevnetConfigmap, pod::StacksDevnetPod, pvc::StacksDevnetPvc, - service::StacksDevnetService, + configmap::StacksDevnetConfigmap, deployment::StacksDevnetDeployment, pod::StacksDevnetPod, + pvc::StacksDevnetPvc, service::StacksDevnetService, stateful_set::StacksDevnetStatefulSet, }; pub mod configmap; +pub mod deployment; pub mod pod; pub mod pvc; pub mod service; +pub mod stateful_set; pub enum StacksDevnetResource { Configmap(StacksDevnetConfigmap), + Deployment(StacksDevnetDeployment), Pod(StacksDevnetPod), Pvc(StacksDevnetPvc), Service(StacksDevnetService), + StatefulSet(StacksDevnetStatefulSet), Namespace, } diff --git a/src/resources/pvc.rs b/src/resources/pvc.rs index 81605e8..58404b4 100644 --- a/src/resources/pvc.rs +++ b/src/resources/pvc.rs @@ -9,7 +9,7 @@ pub enum StacksDevnetPvc { impl fmt::Display for StacksDevnetPvc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api-pg"), + StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api"), } } } diff --git a/src/resources/stateful_set.rs b/src/resources/stateful_set.rs new file mode 100644 index 0000000..c843604 --- /dev/null +++ b/src/resources/stateful_set.rs @@ -0,0 +1,15 @@ +use std::fmt; +use strum_macros::EnumIter; + +#[derive(EnumIter, Debug)] +pub enum StacksDevnetStatefulSet { + StacksBlockchainApi, +} + +impl fmt::Display for StacksDevnetStatefulSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + StacksDevnetStatefulSet::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + } + } +} diff --git a/src/resources/tests.rs b/src/resources/tests.rs index c0ff4d0..38a6b7d 100644 --- a/src/resources/tests.rs +++ b/src/resources/tests.rs @@ -1,6 +1,7 @@ use super::{ - pvc::StacksDevnetPvc, + deployment::StacksDevnetDeployment, service::{get_service_from_path_part, get_service_port, get_user_facing_port, ServicePort}, + stateful_set::StacksDevnetStatefulSet, StacksDevnetConfigmap, StacksDevnetPod, StacksDevnetService, }; use test_case::test_case; @@ -24,9 +25,15 @@ fn it_prints_correct_name_for_pod(pod: StacksDevnetPod) -> String { pod.to_string() } -#[test_case(StacksDevnetPvc::StacksBlockchainApiPg => is equal_to "stacks-blockchain-api-pg".to_string(); "for StacksBlockchainApiPg")] -fn it_prints_correct_name_for_pvc(pvc: StacksDevnetPvc) -> String { - pvc.to_string() +#[test_case(StacksDevnetDeployment::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] +#[test_case(StacksDevnetDeployment::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] +fn it_prints_correct_name_for_deployment(deployment: StacksDevnetDeployment) -> String { + deployment.to_string() +} + +#[test_case(StacksDevnetStatefulSet::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] +fn it_prints_correct_name_for_stateful_set(pod: StacksDevnetStatefulSet) -> String { + pod.to_string() } #[test_case(StacksDevnetService::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] diff --git a/src/routes.rs b/src/routes.rs index 1d807cf..a940e8a 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -63,9 +63,10 @@ pub async fn handle_new_devnet( pub async fn handle_delete_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ) -> Result, Infallible> { - match k8s_manager.delete_devnet(network).await { + match k8s_manager.delete_devnet(network, user_id).await { Ok(_) => responder.ok(), Err(e) => { let msg = format!("error deleting network {}: {}", &network, e.message); @@ -77,10 +78,11 @@ pub async fn handle_delete_devnet( pub async fn handle_get_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ctx: Context, ) -> Result, Infallible> { - match k8s_manager.get_devnet_info(&network).await { + match k8s_manager.get_devnet_info(&network, user_id).await { Ok(devnet_info) => match serde_json::to_vec(&devnet_info) { Ok(body) => responder.ok_with_json(Body::from(body)), Err(e) => { @@ -100,9 +102,13 @@ pub async fn handle_get_devnet( pub async fn handle_check_devnet( k8s_manager: StacksDevnetApiK8sManager, network: &str, + user_id: &str, responder: Responder, ) -> Result, Infallible> { - match k8s_manager.check_any_devnet_assets_exist(&network).await { + match k8s_manager + .check_any_devnet_assets_exist(network, user_id) + .await + { Ok(assets_exist) => match assets_exist { true => responder.ok(), false => responder.err_not_found("not found".to_string()), diff --git a/src/template_parser.rs b/src/template_parser.rs index 521662f..afa97d0 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -1,12 +1,12 @@ use crate::resources::{ - configmap::StacksDevnetConfigmap, pod::StacksDevnetPod, pvc::StacksDevnetPvc, - service::StacksDevnetService, StacksDevnetResource, + configmap::StacksDevnetConfigmap, deployment::StacksDevnetDeployment, + service::StacksDevnetService, stateful_set::StacksDevnetStatefulSet, StacksDevnetResource, }; pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { match resource { - StacksDevnetResource::Pod(StacksDevnetPod::BitcoindNode) => { - include_str!("../templates/pods/bitcoind-chain-coordinator.template.yaml") + StacksDevnetResource::Deployment(StacksDevnetDeployment::BitcoindNode) => { + include_str!("../templates/deployments/bitcoind-chain-coordinator.template.yaml") } StacksDevnetResource::Service(StacksDevnetService::BitcoindNode) => { include_str!("../templates/services/bitcoind-chain-coordinator.template.yaml") @@ -29,27 +29,25 @@ pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApi) => { include_str!("../templates/configmaps/stacks-blockchain-api.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchainApi) => { - include_str!("../templates/pods/stacks-blockchain-api.template.yaml") + StacksDevnetResource::StatefulSet(StacksDevnetStatefulSet::StacksBlockchainApi) => { + include_str!("../templates/stateful-sets/stacks-blockchain-api.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchainApiPg) => { include_str!("../templates/configmaps/stacks-blockchain-api-pg.template.yaml") } - StacksDevnetResource::Pvc(StacksDevnetPvc::StacksBlockchainApiPg) => { - include_str!("../templates/pvcs/stacks-blockchain-api-pg.template.yaml") - } StacksDevnetResource::Service(StacksDevnetService::StacksBlockchainApi) => { include_str!("../templates/services/stacks-blockchain-api.template.yaml") } StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksBlockchain) => { include_str!("../templates/configmaps/stacks-blockchain.template.yaml") } - StacksDevnetResource::Pod(StacksDevnetPod::StacksBlockchain) => { - include_str!("../templates/pods/stacks-blockchain.template.yaml") + StacksDevnetResource::Deployment(StacksDevnetDeployment::StacksBlockchain) => { + include_str!("../templates/deployments/stacks-blockchain.template.yaml") } StacksDevnetResource::Service(StacksDevnetService::StacksBlockchain) => { include_str!("../templates/services/stacks-blockchain.template.yaml") } StacksDevnetResource::Namespace => include_str!("../templates/namespace.template.yaml"), + StacksDevnetResource::Pod(_) | StacksDevnetResource::Pvc(_) => unreachable!(), } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index e4f8b3f..86f3763 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -144,6 +144,7 @@ async fn it_responds_to_valid_requests_with_deploy( let mut config = get_template_config(); config.namespace = namespace.to_owned(); let validated_config = config.to_validated_config(&namespace, ctx.clone()).unwrap(); + let user_id = &namespace; let _ = k8s_manager.deploy_devnet(validated_config).await.unwrap(); // short delay to allow assets to start sleep(Duration::new(5, 0)); @@ -168,7 +169,7 @@ async fn it_responds_to_valid_requests_with_deploy( let mut status = response.status(); if tear_down { - match k8s_manager.delete_devnet(namespace).await { + match k8s_manager.delete_devnet(namespace, user_id).await { Ok(_) => {} Err(e) => { body_str = e.message; diff --git a/templates/ci/stacks-devnet-api.template.yaml b/templates/ci/stacks-devnet-api.template.yaml index 3948dda..1c1b679 100644 --- a/templates/ci/stacks-devnet-api.template.yaml +++ b/templates/ci/stacks-devnet-api.template.yaml @@ -12,7 +12,10 @@ metadata: rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] - verbs: ["get", "delete", "create"] + verbs: ["get", "delete", "create", "list", "deletecollection"] + - apiGroups: ["apps"] + resources: ["deployments", "statefulsets"] + verbs: ["get", "delete", "create", "list"] - apiGroups: [""] resources: ["namespaces"] verbs: ["get"] diff --git a/templates/deployments/bitcoind-chain-coordinator.template.yaml b/templates/deployments/bitcoind-chain-coordinator.template.yaml new file mode 100644 index 0000000..5e747f9 --- /dev/null +++ b/templates/deployments/bitcoind-chain-coordinator.template.yaml @@ -0,0 +1,113 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + name: bitcoind-chain-coordinator + namespace: "{namespace}" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + template: + metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - command: + - /usr/local/bin/bitcoind + - -conf=/etc/bitcoin/bitcoin.conf + - -nodebuglogfile + - -pid=/run/bitcoind.pid + image: quay.io/hirosystems/bitcoind:devnet-v3 + imagePullPolicy: IfNotPresent + name: bitcoind + ports: + - containerPort: 18444 + name: p2p + protocol: TCP + - containerPort: 18443 + name: rpc + protocol: TCP + volumeMounts: + - mountPath: /etc/bitcoin + name: bitcoind + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + - command: + - ./stacks-network + - --namespace=$(NAMESPACE) + - --manifest-path=/etc/stacks-network/project/Clarinet.toml + - --network-manifest-path=/etc/stacks-network/project/settings/Devnet.toml + - --deployment-plan-path=/etc/stacks-network/project/deployments/default.devnet-plan.yaml + - --project-root-path=/etc/stacks-network/project/ + env: + - name: NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + image: hirosystems/stacks-network-orchestrator@sha256:e9c88e46adb10deba74e29883533de747a2200665f919d1708a5ea0f2638d32a + imagePullPolicy: IfNotPresent + name: chain-coordinator + ports: + - containerPort: 20445 + name: coordinator-in + protocol: TCP + - containerPort: 20446 + name: coordinator-con + protocol: TCP + volumeMounts: + - mountPath: /etc/stacks-network/project + name: project-manifest + - mountPath: /etc/stacks-network/project/settings + name: devnet + - mountPath: /etc/stacks-network/project/deployments + name: deployment-plan + - mountPath: /etc/stacks-network/project/contracts + name: project-dir + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + memory: 256Mi + volumes: + - configMap: + name: bitcoind + name: bitcoind + - configMap: + name: project-manifest + name: project-manifest + - configMap: + name: devnet + name: devnet + - configMap: + name: deployment-plan + name: deployment-plan + - configMap: + name: project-dir + name: project-dir \ No newline at end of file diff --git a/templates/deployments/stacks-blockchain.template.yaml b/templates/deployments/stacks-blockchain.template.yaml new file mode 100644 index 0000000..6b7f003 --- /dev/null +++ b/templates/deployments/stacks-blockchain.template.yaml @@ -0,0 +1,69 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + name: stacks-blockchain + namespace: "{namespace}" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + template: + metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - command: + - stacks-node + - start + - --config=/src/stacks-blockchain/Stacks.toml + env: + - name: STACKS_LOG_PP + value: "1" + - name: BLOCKSTACK_USE_TEST_GENESIS_CHAINSTATE + value: "1" + - name: STACKS_LOG_DEBUG + value: "0" + image: quay.io/hirosystems/stacks-node:devnet-v3 + imagePullPolicy: IfNotPresent + name: stacks-blockchain + ports: + - containerPort: 20444 + name: p2p + protocol: TCP + - containerPort: 20443 + name: rpc + protocol: TCP + volumeMounts: + - mountPath: /src/stacks-blockchain + name: stacks-blockchain + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumes: + - configMap: + name: stacks-blockchain + name: stacks-blockchain \ No newline at end of file diff --git a/templates/pods/bitcoind-chain-coordinator.template.yaml b/templates/pods/bitcoind-chain-coordinator.template.yaml deleted file mode 100644 index 4db1864..0000000 --- a/templates/pods/bitcoind-chain-coordinator.template.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: bitcoind-chain-coordinator - name: bitcoind-chain-coordinator - namespace: "{namespace}" -spec: - containers: - - command: - - /usr/local/bin/bitcoind - - -conf=/etc/bitcoin/bitcoin.conf - - -nodebuglogfile - - -pid=/run/bitcoind.pid - image: quay.io/hirosystems/bitcoind:devnet-v3 - imagePullPolicy: IfNotPresent - name: bitcoind - ports: - - containerPort: 18444 - name: p2p - protocol: TCP - - containerPort: 18443 - name: rpc - protocol: TCP - volumeMounts: - - mountPath: /etc/bitcoin - name: bitcoind - readOnly: true - - command: - - ./stacks-network - - --namespace=$(NAMESPACE) - - --manifest-path=/etc/stacks-network/project/Clarinet.toml - - --network-manifest-path=/etc/stacks-network/project/settings/Devnet.toml - - --deployment-plan-path=/etc/stacks-network/project/deployments/default.devnet-plan.yaml - - --project-root-path=/etc/stacks-network/project/ - env: - - name: NAMESPACE - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator:rpc-error - imagePullPolicy: Always - name: chain-coordinator - ports: - - containerPort: 20445 - name: coordinator-in - protocol: TCP - - containerPort: 20446 - name: coordinator-con - protocol: TCP - volumeMounts: - - mountPath: /etc/stacks-network/project - name: project-manifest - - mountPath: /etc/stacks-network/project/settings - name: devnet - - mountPath: /etc/stacks-network/project/deployments - name: deployment-plan - - mountPath: /etc/stacks-network/project/contracts - name: project-dir - volumes: - - configMap: - name: bitcoind - name: bitcoind - - configMap: - name: project-manifest - name: project-manifest - - configMap: - name: devnet - name: devnet - - configMap: - name: deployment-plan - name: deployment-plan - - configMap: - name: project-dir - name: project-dir diff --git a/templates/pods/stacks-blockchain-api.template.yaml b/templates/pods/stacks-blockchain-api.template.yaml deleted file mode 100644 index 94f58de..0000000 --- a/templates/pods/stacks-blockchain-api.template.yaml +++ /dev/null @@ -1,42 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: stacks-blockchain-api - name: stacks-blockchain-api - namespace: "{namespace}" -spec: - containers: - - name: stacks-blockchain-api - envFrom: - - configMapRef: - name: stacks-blockchain-api - optional: false - image: hirosystems/stacks-blockchain-api - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3999 - name: api - protocol: TCP - - containerPort: 3700 - name: eventport - protocol: TCP - - name: postgres - envFrom: - - configMapRef: - name: stacks-blockchain-api-pg - optional: false - image: postgres:14 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 5432 - name: postgres - protocol: TCP - volumeMounts: - - mountPath: /var/lib/postgresql/data - name: stacks-blockchain-api-pg - subPath: postgres - volumes: - - name: stacks-blockchain-api-pg - persistentVolumeClaim: - claimName: stacks-blockchain-api-pg diff --git a/templates/pods/stacks-blockchain.template.yaml b/templates/pods/stacks-blockchain.template.yaml deleted file mode 100644 index 421c4a2..0000000 --- a/templates/pods/stacks-blockchain.template.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - name: stacks-blockchain - name: stacks-blockchain - namespace: "{namespace}" -spec: - containers: - - command: - - stacks-node - - start - - --config=/src/stacks-blockchain/Stacks.toml - env: - - name: STACKS_LOG_PP - value: "1" - - name: BLOCKSTACK_USE_TEST_GENESIS_CHAINSTATE - value: "1" - - name: STACKS_LOG_DEBUG - value: "0" - image: quay.io/hirosystems/stacks-node:devnet-v3 - imagePullPolicy: IfNotPresent - name: stacks-blockchain - ports: - - containerPort: 20444 - name: p2p - protocol: TCP - - containerPort: 20443 - name: rpc - protocol: TCP - volumeMounts: - - mountPath: /src/stacks-blockchain - name: stacks-blockchain - readOnly: true - volumes: - - configMap: - name: stacks-blockchain - name: stacks-blockchain \ No newline at end of file diff --git a/templates/pvcs/stacks-blockchain-api-pg.template.yaml b/templates/pvcs/stacks-blockchain-api-pg.template.yaml deleted file mode 100644 index 97df3f3..0000000 --- a/templates/pvcs/stacks-blockchain-api-pg.template.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: stacks-blockchain-api-pg - namespace: "{namespace}" -spec: - accessModes: - - ReadWriteOnce - resources: - limits: - storage: 750Mi - requests: - storage: 500Mi - storageClassName: premium-rwo - volumeMode: Filesystem diff --git a/templates/services/bitcoind-chain-coordinator.template.yaml b/templates/services/bitcoind-chain-coordinator.template.yaml index 9c782e0..ddcaac7 100644 --- a/templates/services/bitcoind-chain-coordinator.template.yaml +++ b/templates/services/bitcoind-chain-coordinator.template.yaml @@ -1,25 +1,33 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator name: bitcoind-chain-coordinator namespace: "{namespace}" spec: ports: - - name: p2p + - name: tcp-p2p port: 18444 protocol: TCP targetPort: 18444 - - name: rpc + - name: tcp-rpc port: 18443 protocol: TCP targetPort: 18443 - - name: coordinator-in + - name: http-coordinator-in port: 20445 protocol: TCP targetPort: 20445 - - name: coordinator-con + - name: http-coordinator-con port: 20446 protocol: TCP targetPort: 20446 selector: - name: bitcoind-chain-coordinator + app.kubernetes.io/component: bitcoind-chain-coordinator + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: bitcoind-chain-coordinator diff --git a/templates/services/stacks-blockchain-api.template.yaml b/templates/services/stacks-blockchain-api.template.yaml index ea5a95b..02f9456 100644 --- a/templates/services/stacks-blockchain-api.template.yaml +++ b/templates/services/stacks-blockchain-api.template.yaml @@ -1,21 +1,29 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api name: stacks-blockchain-api namespace: "{namespace}" spec: ports: - - name: api + - name: http-api port: 3999 protocol: TCP targetPort: 3999 - - name: postgres + - name: tcp-postgres port: 5432 protocol: TCP targetPort: 5432 - - name: eventport + - name: tcp-eventport port: 3700 protocol: TCP targetPort: 3700 selector: - name: stacks-blockchain-api + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api diff --git a/templates/services/stacks-blockchain.template.yaml b/templates/services/stacks-blockchain.template.yaml index 0daa77e..da0c733 100644 --- a/templates/services/stacks-blockchain.template.yaml +++ b/templates/services/stacks-blockchain.template.yaml @@ -1,15 +1,23 @@ apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain name: stacks-blockchain namespace: "{namespace}" spec: ports: - - name: p2p + - name: tcp-p2p port: 20444 protocol: TCP - - name: rpc + - name: http-rpc port: 20443 protocol: TCP selector: - name: stacks-blockchain + app.kubernetes.io/component: stacks-blockchain + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain diff --git a/templates/stacks-devnet-api.template.yaml b/templates/stacks-devnet-api.template.yaml index cf2e91f..2f2727d 100644 --- a/templates/stacks-devnet-api.template.yaml +++ b/templates/stacks-devnet-api.template.yaml @@ -12,7 +12,10 @@ metadata: rules: - apiGroups: [""] resources: ["pods", "pods/status", "services", "configmaps", "persistentvolumeclaims"] - verbs: ["get", "delete", "create"] + verbs: ["get", "delete", "create", "list", "deletecollection"] + - apiGroups: ["apps"] + resources: ["deployments", "statefulsets"] + verbs: ["get", "delete", "create", "list"] - apiGroups: [""] resources: ["namespaces"] verbs: ["get"] diff --git a/templates/stateful-sets/stacks-blockchain-api.template.yaml b/templates/stateful-sets/stacks-blockchain-api.template.yaml new file mode 100644 index 0000000..81e8101 --- /dev/null +++ b/templates/stateful-sets/stacks-blockchain-api.template.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + name: stacks-blockchain-api + namespace: "{namespace}" +spec: + replicas: 1 + serviceName: stacks-blockchain-api + selector: + matchLabels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + template: + metadata: + labels: + app.kubernetes.io/component: stacks-blockchain-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-blockchain-api + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - name: stacks-blockchain-api + envFrom: + - configMapRef: + name: stacks-blockchain-api + optional: false + image: hirosystems/stacks-blockchain-api + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3999 + name: api + protocol: TCP + - containerPort: 3700 + name: eventport + protocol: TCP + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + - name: postgres + envFrom: + - configMapRef: + name: stacks-blockchain-api-pg + optional: false + image: postgres:15 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5432 + name: postgres + protocol: TCP + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: pg + subPath: postgres + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + memory: 512Mi + volumeClaimTemplates: + - metadata: + name: pg + spec: + accessModes: + - ReadWriteOnce + storageClassName: premium-rwo + resources: + requests: + storage: 1Gi \ No newline at end of file From 0217bfd00476b291905c3755cf1b321452fe5200 Mon Sep 17 00:00:00 2001 From: deantchi <21262275+deantchi@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:11:29 -0700 Subject: [PATCH 27/35] chore: add label to all configmaps (#80) --- templates/configmaps/bitcoind.template.yaml | 3 +++ .../configmaps/chain-coord-deployment-plan.template.yaml | 2 ++ templates/configmaps/chain-coord-devnet.template.yaml | 5 +++++ templates/configmaps/chain-coord-project-dir.template.yaml | 5 +++++ .../configmaps/chain-coord-project-manifest.template.yaml | 5 +++++ templates/configmaps/stacks-blockchain-api-pg.template.yaml | 5 +++++ templates/configmaps/stacks-blockchain-api.template.yaml | 5 +++++ templates/configmaps/stacks-blockchain.template.yaml | 5 +++++ 8 files changed, 35 insertions(+) diff --git a/templates/configmaps/bitcoind.template.yaml b/templates/configmaps/bitcoind.template.yaml index 55fce72..0fa9a81 100644 --- a/templates/configmaps/bitcoind.template.yaml +++ b/templates/configmaps/bitcoind.template.yaml @@ -5,3 +5,6 @@ kind: ConfigMap metadata: name: bitcoind namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + diff --git a/templates/configmaps/chain-coord-deployment-plan.template.yaml b/templates/configmaps/chain-coord-deployment-plan.template.yaml index d505124..a733b8c 100644 --- a/templates/configmaps/chain-coord-deployment-plan.template.yaml +++ b/templates/configmaps/chain-coord-deployment-plan.template.yaml @@ -5,3 +5,5 @@ kind: ConfigMap metadata: name: deployment-plan namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api diff --git a/templates/configmaps/chain-coord-devnet.template.yaml b/templates/configmaps/chain-coord-devnet.template.yaml index 7397da1..3386eb9 100644 --- a/templates/configmaps/chain-coord-devnet.template.yaml +++ b/templates/configmaps/chain-coord-devnet.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: devnet namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: devnet + app.kubernetes.io/component: devnet diff --git a/templates/configmaps/chain-coord-project-dir.template.yaml b/templates/configmaps/chain-coord-project-dir.template.yaml index c6b8e81..b71e4c9 100644 --- a/templates/configmaps/chain-coord-project-dir.template.yaml +++ b/templates/configmaps/chain-coord-project-dir.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: project-dir namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: project-dir + app.kubernetes.io/component: project-dir diff --git a/templates/configmaps/chain-coord-project-manifest.template.yaml b/templates/configmaps/chain-coord-project-manifest.template.yaml index c657e1e..7c7a85f 100644 --- a/templates/configmaps/chain-coord-project-manifest.template.yaml +++ b/templates/configmaps/chain-coord-project-manifest.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: project-manifest namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: project-manifest + app.kubernetes.io/component: project-manifest diff --git a/templates/configmaps/stacks-blockchain-api-pg.template.yaml b/templates/configmaps/stacks-blockchain-api-pg.template.yaml index 8e15c54..fe996d7 100644 --- a/templates/configmaps/stacks-blockchain-api-pg.template.yaml +++ b/templates/configmaps/stacks-blockchain-api-pg.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: stacks-blockchain-api-pg namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: stacks-blockchain-api-pg + app.kubernetes.io/component: stacks-blockchain-api-pg \ No newline at end of file diff --git a/templates/configmaps/stacks-blockchain-api.template.yaml b/templates/configmaps/stacks-blockchain-api.template.yaml index 4027081..ed691f5 100644 --- a/templates/configmaps/stacks-blockchain-api.template.yaml +++ b/templates/configmaps/stacks-blockchain-api.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: stacks-blockchain-api namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: stacks-blockchain-api + app.kubernetes.io/component: stacks-blockchain-api diff --git a/templates/configmaps/stacks-blockchain.template.yaml b/templates/configmaps/stacks-blockchain.template.yaml index a50cfd5..e15941f 100644 --- a/templates/configmaps/stacks-blockchain.template.yaml +++ b/templates/configmaps/stacks-blockchain.template.yaml @@ -5,3 +5,8 @@ kind: ConfigMap metadata: name: stacks-blockchain namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/name: stacks-blockchain + app.kubernetes.io/component: stacks-blockchain From e92dcb57d1e0ad5b1a11fc1403daf06f10706da9 Mon Sep 17 00:00:00 2001 From: deantchi <21262275+deantchi@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:30:11 -0700 Subject: [PATCH 28/35] fix: removing untemplated labels (#82) --- templates/configmaps/bitcoind.template.yaml | 3 ++- templates/configmaps/chain-coord-deployment-plan.template.yaml | 2 ++ templates/configmaps/chain-coord-devnet.template.yaml | 1 - templates/configmaps/chain-coord-project-dir.template.yaml | 1 - .../configmaps/chain-coord-project-manifest.template.yaml | 1 - templates/configmaps/stacks-blockchain-api-pg.template.yaml | 1 - templates/configmaps/stacks-blockchain-api.template.yaml | 1 - templates/configmaps/stacks-blockchain.template.yaml | 1 - 8 files changed, 4 insertions(+), 7 deletions(-) diff --git a/templates/configmaps/bitcoind.template.yaml b/templates/configmaps/bitcoind.template.yaml index 0fa9a81..5c0d6ff 100644 --- a/templates/configmaps/bitcoind.template.yaml +++ b/templates/configmaps/bitcoind.template.yaml @@ -7,4 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - + app.kubernetes.io/name: bitcoind + app.kubernetes.io/component: bitcoind diff --git a/templates/configmaps/chain-coord-deployment-plan.template.yaml b/templates/configmaps/chain-coord-deployment-plan.template.yaml index a733b8c..5a8313f 100644 --- a/templates/configmaps/chain-coord-deployment-plan.template.yaml +++ b/templates/configmaps/chain-coord-deployment-plan.template.yaml @@ -7,3 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: deployment-plan + app.kubernetes.io/component: deployment-plan diff --git a/templates/configmaps/chain-coord-devnet.template.yaml b/templates/configmaps/chain-coord-devnet.template.yaml index 3386eb9..c9a0bba 100644 --- a/templates/configmaps/chain-coord-devnet.template.yaml +++ b/templates/configmaps/chain-coord-devnet.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: devnet app.kubernetes.io/component: devnet diff --git a/templates/configmaps/chain-coord-project-dir.template.yaml b/templates/configmaps/chain-coord-project-dir.template.yaml index b71e4c9..5af5294 100644 --- a/templates/configmaps/chain-coord-project-dir.template.yaml +++ b/templates/configmaps/chain-coord-project-dir.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: project-dir app.kubernetes.io/component: project-dir diff --git a/templates/configmaps/chain-coord-project-manifest.template.yaml b/templates/configmaps/chain-coord-project-manifest.template.yaml index 7c7a85f..5ca44ae 100644 --- a/templates/configmaps/chain-coord-project-manifest.template.yaml +++ b/templates/configmaps/chain-coord-project-manifest.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: project-manifest app.kubernetes.io/component: project-manifest diff --git a/templates/configmaps/stacks-blockchain-api-pg.template.yaml b/templates/configmaps/stacks-blockchain-api-pg.template.yaml index fe996d7..52a65c1 100644 --- a/templates/configmaps/stacks-blockchain-api-pg.template.yaml +++ b/templates/configmaps/stacks-blockchain-api-pg.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: stacks-blockchain-api-pg app.kubernetes.io/component: stacks-blockchain-api-pg \ No newline at end of file diff --git a/templates/configmaps/stacks-blockchain-api.template.yaml b/templates/configmaps/stacks-blockchain-api.template.yaml index ed691f5..8be25b5 100644 --- a/templates/configmaps/stacks-blockchain-api.template.yaml +++ b/templates/configmaps/stacks-blockchain-api.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: stacks-blockchain-api app.kubernetes.io/component: stacks-blockchain-api diff --git a/templates/configmaps/stacks-blockchain.template.yaml b/templates/configmaps/stacks-blockchain.template.yaml index e15941f..8e7e9cb 100644 --- a/templates/configmaps/stacks-blockchain.template.yaml +++ b/templates/configmaps/stacks-blockchain.template.yaml @@ -7,6 +7,5 @@ metadata: namespace: "{namespace}" labels: app.kubernetes.io/managed-by: stacks-devnet-api - app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/name: stacks-blockchain app.kubernetes.io/component: stacks-blockchain From dbae954d9d952a3615da61c5a656f1eedc9020a8 Mon Sep 17 00:00:00 2001 From: Charlie <2747302+CharlieC3@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:52:21 -0500 Subject: [PATCH 29/35] chore: update argocd labels --- templates/configmaps/bitcoind.template.yaml | 1 + templates/configmaps/chain-coord-deployment-plan.template.yaml | 1 + templates/configmaps/chain-coord-devnet.template.yaml | 1 + templates/configmaps/chain-coord-project-dir.template.yaml | 1 + .../configmaps/chain-coord-project-manifest.template.yaml | 1 + templates/configmaps/stacks-blockchain-api-pg.template.yaml | 3 ++- templates/configmaps/stacks-blockchain-api.template.yaml | 1 + templates/configmaps/stacks-blockchain.template.yaml | 2 ++ templates/deployments/bitcoind-chain-coordinator.template.yaml | 1 + templates/deployments/stacks-blockchain.template.yaml | 1 + templates/services/bitcoind-chain-coordinator.template.yaml | 1 + templates/services/stacks-blockchain-api.template.yaml | 1 + templates/services/stacks-blockchain.template.yaml | 1 + templates/stateful-sets/stacks-blockchain-api.template.yaml | 1 + 14 files changed, 16 insertions(+), 1 deletion(-) diff --git a/templates/configmaps/bitcoind.template.yaml b/templates/configmaps/bitcoind.template.yaml index 5c0d6ff..d2fc4ac 100644 --- a/templates/configmaps/bitcoind.template.yaml +++ b/templates/configmaps/bitcoind.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: bitcoind app.kubernetes.io/component: bitcoind + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/chain-coord-deployment-plan.template.yaml b/templates/configmaps/chain-coord-deployment-plan.template.yaml index 5a8313f..47810d3 100644 --- a/templates/configmaps/chain-coord-deployment-plan.template.yaml +++ b/templates/configmaps/chain-coord-deployment-plan.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: deployment-plan app.kubernetes.io/component: deployment-plan + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/chain-coord-devnet.template.yaml b/templates/configmaps/chain-coord-devnet.template.yaml index c9a0bba..63f1e56 100644 --- a/templates/configmaps/chain-coord-devnet.template.yaml +++ b/templates/configmaps/chain-coord-devnet.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: devnet app.kubernetes.io/component: devnet + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/chain-coord-project-dir.template.yaml b/templates/configmaps/chain-coord-project-dir.template.yaml index 5af5294..dd78f61 100644 --- a/templates/configmaps/chain-coord-project-dir.template.yaml +++ b/templates/configmaps/chain-coord-project-dir.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: project-dir app.kubernetes.io/component: project-dir + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/chain-coord-project-manifest.template.yaml b/templates/configmaps/chain-coord-project-manifest.template.yaml index 5ca44ae..f41d0e9 100644 --- a/templates/configmaps/chain-coord-project-manifest.template.yaml +++ b/templates/configmaps/chain-coord-project-manifest.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: project-manifest app.kubernetes.io/component: project-manifest + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/stacks-blockchain-api-pg.template.yaml b/templates/configmaps/stacks-blockchain-api-pg.template.yaml index 52a65c1..1cdd8ca 100644 --- a/templates/configmaps/stacks-blockchain-api-pg.template.yaml +++ b/templates/configmaps/stacks-blockchain-api-pg.template.yaml @@ -8,4 +8,5 @@ metadata: labels: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain-api-pg - app.kubernetes.io/component: stacks-blockchain-api-pg \ No newline at end of file + app.kubernetes.io/component: stacks-blockchain-api-pg + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/stacks-blockchain-api.template.yaml b/templates/configmaps/stacks-blockchain-api.template.yaml index 8be25b5..162af14 100644 --- a/templates/configmaps/stacks-blockchain-api.template.yaml +++ b/templates/configmaps/stacks-blockchain-api.template.yaml @@ -9,3 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain-api app.kubernetes.io/component: stacks-blockchain-api + argocd.argoproj.io/instance: platform-user-resources.platform diff --git a/templates/configmaps/stacks-blockchain.template.yaml b/templates/configmaps/stacks-blockchain.template.yaml index 8e7e9cb..ea7ebcc 100644 --- a/templates/configmaps/stacks-blockchain.template.yaml +++ b/templates/configmaps/stacks-blockchain.template.yaml @@ -9,3 +9,5 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain app.kubernetes.io/component: stacks-blockchain + argocd.argoproj.io/instance: platform-user-resources.platform +∑ \ No newline at end of file diff --git a/templates/deployments/bitcoind-chain-coordinator.template.yaml b/templates/deployments/bitcoind-chain-coordinator.template.yaml index 5e747f9..2f1ac1a 100644 --- a/templates/deployments/bitcoind-chain-coordinator.template.yaml +++ b/templates/deployments/bitcoind-chain-coordinator.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: bitcoind-chain-coordinator + argocd.argoproj.io/instance: platform-user-resources.platform name: bitcoind-chain-coordinator namespace: "{namespace}" spec: diff --git a/templates/deployments/stacks-blockchain.template.yaml b/templates/deployments/stacks-blockchain.template.yaml index 6b7f003..824c1a2 100644 --- a/templates/deployments/stacks-blockchain.template.yaml +++ b/templates/deployments/stacks-blockchain.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain + argocd.argoproj.io/instance: platform-user-resources.platform name: stacks-blockchain namespace: "{namespace}" spec: diff --git a/templates/services/bitcoind-chain-coordinator.template.yaml b/templates/services/bitcoind-chain-coordinator.template.yaml index ddcaac7..e0e3c45 100644 --- a/templates/services/bitcoind-chain-coordinator.template.yaml +++ b/templates/services/bitcoind-chain-coordinator.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: bitcoind-chain-coordinator + argocd.argoproj.io/instance: platform-user-resources.platform name: bitcoind-chain-coordinator namespace: "{namespace}" spec: diff --git a/templates/services/stacks-blockchain-api.template.yaml b/templates/services/stacks-blockchain-api.template.yaml index 02f9456..d53e448 100644 --- a/templates/services/stacks-blockchain-api.template.yaml +++ b/templates/services/stacks-blockchain-api.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain-api + argocd.argoproj.io/instance: platform-user-resources.platform name: stacks-blockchain-api namespace: "{namespace}" spec: diff --git a/templates/services/stacks-blockchain.template.yaml b/templates/services/stacks-blockchain.template.yaml index da0c733..4391fea 100644 --- a/templates/services/stacks-blockchain.template.yaml +++ b/templates/services/stacks-blockchain.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain + argocd.argoproj.io/instance: platform-user-resources.platform name: stacks-blockchain namespace: "{namespace}" spec: diff --git a/templates/stateful-sets/stacks-blockchain-api.template.yaml b/templates/stateful-sets/stacks-blockchain-api.template.yaml index 81e8101..cb0ba35 100644 --- a/templates/stateful-sets/stacks-blockchain-api.template.yaml +++ b/templates/stateful-sets/stacks-blockchain-api.template.yaml @@ -6,6 +6,7 @@ metadata: app.kubernetes.io/instance: "{user_id}" app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain-api + argocd.argoproj.io/instance: platform-user-resources.platform name: stacks-blockchain-api namespace: "{namespace}" spec: From 4399177d702d4f8865ebf410cbbc1280a2706f72 Mon Sep 17 00:00:00 2001 From: Ludo Galabru Date: Thu, 16 Nov 2023 13:50:48 -0500 Subject: [PATCH 30/35] fix: template (#85) --- templates/configmaps/stacks-blockchain.template.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/configmaps/stacks-blockchain.template.yaml b/templates/configmaps/stacks-blockchain.template.yaml index ea7ebcc..a4c80b1 100644 --- a/templates/configmaps/stacks-blockchain.template.yaml +++ b/templates/configmaps/stacks-blockchain.template.yaml @@ -9,5 +9,4 @@ metadata: app.kubernetes.io/managed-by: stacks-devnet-api app.kubernetes.io/name: stacks-blockchain app.kubernetes.io/component: stacks-blockchain - argocd.argoproj.io/instance: platform-user-resources.platform -∑ \ No newline at end of file + argocd.argoproj.io/instance: platform-user-resources.platform \ No newline at end of file From dff55981e5ff80ed584b676307547108d6c4db55 Mon Sep 17 00:00:00 2001 From: CharlieC3 <2747302+CharlieC3@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:22:00 -0500 Subject: [PATCH 31/35] ci: update auto-approve and fix releasing to a protected branch --- .github/workflows/ci.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9880335..c766211 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -141,6 +141,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} persist-credentials: false - name: Semantic Release @@ -148,7 +149,7 @@ jobs: id: semantic if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} SEMANTIC_RELEASE_PACKAGE: ${{ github.event.repository.name }} with: semantic_version: 19 @@ -234,9 +235,9 @@ jobs: - name: Approve pending deployment run: | sleep 5 - ENV_ID=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') + ENV_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') if [[ -n "${ENV_ID}" ]]; then - curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" + curl -s -X POST -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" fi deploy-staging: @@ -277,9 +278,9 @@ jobs: - name: Approve pending deployment run: | sleep 5 - ENV_ID=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') + ENV_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" | jq -r '.[0].environment.id // empty') if [[ -n "${ENV_ID}" ]]; then - curl -s -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" + curl -s -X POST -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/hirosystems/stacks-devnet-api/actions/runs/${{ github.run_id }}/pending_deployments" -d "{\"environment_ids\":[${ENV_ID}],\"state\":\"approved\",\"comment\":\"auto approve\"}" fi deploy-prod: From 47417d5c4cf42df1b4b08d7c973e38153cea7107 Mon Sep 17 00:00:00 2001 From: CharlieC3 <2747302+CharlieC3@users.noreply.github.com> Date: Tue, 2 Jan 2024 12:12:30 -0500 Subject: [PATCH 32/35] ci: update continuous deployment jobs --- .github/workflows/ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c766211..87beb42 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -220,7 +220,7 @@ jobs: - name: Deploy Stacks Devnet API uses: ./actions/deploy with: - docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + docker_image_tag_or_digest: ${{ needs.build-publish-release.outputs.docker_image_digest }} k8s_repo: k8s-platform k8s_branch: main file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml @@ -262,7 +262,7 @@ jobs: - name: Deploy Stacks Devnet API uses: ./actions/deploy with: - docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + docker_image_tag_or_digest: ${{ needs.build-publish-release.outputs.docker_image_digest }} k8s_repo: k8s-platform k8s_branch: main file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml @@ -305,7 +305,7 @@ jobs: - name: Deploy Stacks Devnet API uses: ./actions/deploy with: - docker_tag: ${{ needs.build-publish-release.outputs.docker_image_digest }} + docker_image_tag_or_digest: ${{ needs.build-publish-release.outputs.docker_image_digest }} k8s_repo: k8s-platform k8s_branch: main file_pattern: manifests/api/stacks-devnet-api/${{ env.DEPLOY_ENV }}/base/kustomization.yaml From 521acd12d61ec6f33c520bd38e4391dc0c09ce4f Mon Sep 17 00:00:00 2001 From: Micaiah Reid Date: Mon, 25 Mar 2024 16:50:20 -0400 Subject: [PATCH 33/35] feat: upgrade clarinet to v2.4.0; enable `use_nakamoto` field to configure stacks chain for nakamoto (#89) --- Cargo.lock | 2496 +++++++++++++---- Cargo.toml | 20 +- Dockerfile | 3 +- rust-toolchain | 2 + src/config.rs | 2 +- src/lib.rs | 393 ++- src/resources/configmap.rs | 4 + src/resources/deployment.rs | 2 +- src/resources/pod.rs | 4 + src/resources/pvc.rs | 4 + src/resources/service.rs | 11 + src/resources/stateful_set.rs | 21 +- src/resources/tests.rs | 18 +- src/template_parser.rs | 18 + src/tests/fixtures/network-manifest.yaml | 9 +- src/tests/fixtures/project-manifest.yaml | 2 + src/tests/fixtures/stacks-devnet-config.json | 12 +- src/tests/mod.rs | 41 +- .../configmaps/stacks-signer-0.template.yaml | 12 + .../configmaps/stacks-signer-1.template.yaml | 12 + .../bitcoind-chain-coordinator.template.yaml | 4 +- .../stacks-blockchain.template.yaml | 7 +- .../services/stacks-signer-0.template.yaml | 22 + .../services/stacks-signer-1.template.yaml | 22 + .../stacks-signer-0.template.yaml | 79 + .../stacks-signer-1.template.yaml | 79 + 26 files changed, 2714 insertions(+), 585 deletions(-) create mode 100644 rust-toolchain create mode 100644 templates/configmaps/stacks-signer-0.template.yaml create mode 100644 templates/configmaps/stacks-signer-1.template.yaml create mode 100644 templates/services/stacks-signer-0.template.yaml create mode 100644 templates/services/stacks-signer-1.template.yaml create mode 100644 templates/stateful-sets/stacks-signer-0.template.yaml create mode 100644 templates/stateful-sets/stacks-signer-1.template.yaml diff --git a/Cargo.lock b/Cargo.lock index d78dc7a..4116def 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,16 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ - "gimli", + "gimli 0.27.3", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.1", ] [[package]] @@ -18,26 +27,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "ahash" -version = "0.7.6" +name = "aead" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "getrandom 0.2.10", - "once_cell", - "version_check", + "crypto-common", + "generic-array 0.14.7", ] [[package]] -name = "ahash" +name = "aes" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle 2.5.0", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom 0.2.10", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -49,6 +83,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -73,6 +113,66 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arc-swap" version = "1.6.0" @@ -85,6 +185,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-stream" version = "0.3.5" @@ -104,7 +210,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -115,7 +221,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -152,21 +258,15 @@ version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ - "addr2line", + "addr2line 0.20.0", "cc", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.31.1", "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base58" version = "0.1.0" @@ -218,6 +318,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bip39" version = "1.2.0" @@ -281,9 +390,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] [[package]] name = "block-buffer" @@ -291,7 +412,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5", + "block-padding", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -303,7 +424,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding 0.2.1", "generic-array 0.14.7", ] @@ -326,10 +446,19 @@ dependencies = [ ] [[package]] -name = "block-padding" -version = "0.2.1" +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] [[package]] name = "bumpalo" @@ -337,6 +466,12 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "byte-tools" version = "0.3.1" @@ -360,6 +495,9 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-if" @@ -369,9 +507,8 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chainhook-types" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65268eb9aad0d567865cd13c5a5c7a9a301d99f6301f8f5cac1654744eda0a7c" +version = "1.3.3" +source = "git+https://github.com/hirosystems/chainhook.git?branch=chore/update-clarinet-and-clarity#160fd65a66a679efa4cf19cbe33c685eebaa9c2d" dependencies = [ "hex", "schemars", @@ -383,25 +520,88 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets 0.48.1", ] [[package]] -name = "clarinet-deployments" -version = "1.0.3" +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b484bdc8880ac7cc3fff38d1cd17d957685af68f8dde2bba82878c48381bb4" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clar2wasm" +version = "0.1.0" +source = "git+https://github.com/stacks-network/clarity-wasm.git#5edf193d8ec7c3a60676cfa3e079af126aaf99b6" +dependencies = [ + "clap", + "clarity", + "lazy_static", + "regex", + "stacks-common", + "walrus", + "wasmtime", + "wat", +] + +[[package]] +name = "clarinet-deployments" +version = "2.4.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" dependencies = [ "base58 0.2.0", "base64 0.21.4", @@ -411,6 +611,7 @@ dependencies = [ "clarinet-files", "clarinet-utils", "clarity-repl", + "colored", "libsecp256k1 0.7.1", "reqwest", "serde", @@ -423,9 +624,8 @@ dependencies = [ [[package]] name = "clarinet-files" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc81af3686bcf35e8f8bfe844473f1af3372a7e148561378f6598a871dc018b" +version = "2.4.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" dependencies = [ "bip39", "bitcoin", @@ -437,45 +637,65 @@ dependencies = [ "serde_derive", "serde_json", "tiny-hderive", - "toml", + "toml 0.5.11", "url", ] [[package]] name = "clarinet-utils" version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f19b3340d53afe73fb175052ab09aec54b6e3076afd30d6b907b401cb6eecf3c" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" dependencies = [ "hmac 0.12.1", "pbkdf2", - "serde", "sha2 0.10.7", ] +[[package]] +name = "clarity" +version = "2.3.0" +source = "git+https://github.com/stacks-network/stacks-core.git?branch=feat/clarity-wasm-next#5eb967cf4f58efcffe385c7a6fd2ed82e5f5da58" +dependencies = [ + "hashbrown 0.14.3", + "integer-sqrt", + "lazy_static", + "rand 0.8.5", + "rand_chacha 0.3.1", + "regex", + "rusqlite", + "serde", + "serde_derive", + "serde_json", + "serde_stacker", + "sha2-asm", + "slog", + "stacks-common", + "wasmtime", +] + [[package]] name = "clarity-repl" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce75a1ed41a078a67f78abe73c84c241331044304831af8db8b7056dcef46fc" +version = "2.4.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" dependencies = [ "ansi_term", "atty", "bytes", "chrono", - "clarity-vm", + "clar2wasm", + "clarity", "debug_types", "futures", "getrandom 0.2.10", - "hiro-system-kit", + "hiro-system-kit 0.1.0 (git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908)", "httparse", "integer-sqrt", "lazy_static", "log", "memchr", "pico-args", + "pox-locking", "prettytable-rs", - "rand 0.7.3", "regex", "reqwest", "rustyline", @@ -483,60 +703,43 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.7", - "sha3 0.9.1", "tokio", "tokio-util", + "wsts", ] [[package]] -name = "clarity-vm" -version = "2.1.1" +name = "clipboard-win" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cebf93044798d7729008e9a467b16578aead7f0b3568219a2b20b421b683db" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ - "integer-sqrt", - "lazy_static", - "rand 0.7.3", - "rand_chacha 0.2.2", - "regex", - "rstest", - "rstest_reuse", - "rusqlite", - "serde", - "serde_derive", - "serde_json", - "serde_stacker", - "sha2-asm", - "slog", - "stacks-common", - "time 0.2.27", + "error-code", + "str-buf", + "winapi", ] [[package]] -name = "clear_on_drop" -version = "0.2.5" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" -dependencies = [ - "cc", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "clipboard-win" -version = "4.5.0" +name = "colored" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "error-code", - "str-buf", - "winapi", + "lazy_static", + "windows-sys 0.48.0", ] [[package]] -name = "const_fn" -version = "0.4.9" +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "core-foundation-sys" @@ -544,6 +747,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -554,81 +766,233 @@ dependencies = [ ] [[package]] -name = "crossbeam-channel" -version = "0.5.8" +name = "cranelift-bforest" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "8e7e56668d2263f92b691cb9e4a2fcb186ca0384941fe420484322fa559c3329" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cranelift-entity", ] [[package]] -name = "crossbeam-utils" -version = "0.8.16" +name = "cranelift-codegen" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "2a9ff61938bf11615f55b80361288c68865318025632ea73c65c0b44fa16283c" dependencies = [ - "cfg-if", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.28.1", + "hashbrown 0.14.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", ] [[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" +name = "cranelift-codegen-meta" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "50656bf19e3d4a153b404ff835b8b59e924cfa3682ebe0d3df408994f37983f6" dependencies = [ - "generic-array 0.14.7", - "typenum", + "cranelift-codegen-shared", ] [[package]] -name = "crypto-mac" -version = "0.7.0" +name = "cranelift-codegen-shared" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] +checksum = "388041deeb26109f1ea73c1812ea26bfd406c94cbce0bb5230aa44277e43b209" [[package]] -name = "crypto-mac" -version = "0.8.0" +name = "cranelift-control" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "b39b7c512ffac527e5b5df9beae3d67ab85d07dca6d88942c16195439fedd1d3" dependencies = [ - "generic-array 0.14.7", - "subtle 2.5.0", + "arbitrary", ] [[package]] -name = "csv" -version = "1.2.2" +name = "cranelift-entity" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +checksum = "fdb25f573701284fe2bcf88209d405342125df00764b396c923e11eafc94d892" dependencies = [ - "csv-core", - "itoa", - "ryu", "serde", + "serde_derive", ] [[package]] -name = "csv-core" -version = "0.1.10" +name = "cranelift-frontend" +version = "0.102.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "e57374fd11d72cf9ffb85ff64506ed831440818318f58d09f45b4185e5e9c376" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae769b235f6ea2f86623a3ff157cc04a4ff131dc9fe782c2ebd35f272043581e" + +[[package]] +name = "cranelift-native" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc7bfb8f13a0526fe20db338711d9354729b861c336978380bb10f7f17dd207" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5f41a4af931b756be05af0dd374ce200aae2d52cea16b0beb07e8b52732c35" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.116.1", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array 0.12.4", + "subtle 1.0.0", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle 2.5.0", +] + +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "2.0.0" @@ -643,6 +1007,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle 2.5.0", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + [[package]] name = "dashmap" version = "5.5.0" @@ -650,7 +1042,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "lock_api", "once_cell", "parking_lot_core", @@ -667,6 +1059,34 @@ dependencies = [ "serde_json", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -707,6 +1127,16 @@ dependencies = [ "subtle 2.5.0", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -728,29 +1158,36 @@ dependencies = [ "winapi", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dyn-clone" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "serde", + "signature", +] + [[package]] name = "ed25519-dalek" -version = "1.0.0-pre.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "clear_on_drop", - "curve25519-dalek", - "rand 0.7.3", + "curve25519-dalek 4.1.2", + "ed25519", + "rand_core 0.6.4", "serde", - "sha2 0.8.2", + "sha2 0.10.7", + "subtle 2.5.0", + "zeroize", ] [[package]] @@ -788,23 +1225,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -829,12 +1255,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fallible-streaming-iterator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fd-lock" version = "3.0.13" @@ -843,7 +1281,25 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", ] [[package]] @@ -876,6 +1332,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.28" @@ -932,7 +1394,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -965,6 +1427,28 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.4.1", + "debugid", + "fxhash", + "serde", + "serde_json", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -1008,12 +1492,44 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator 0.2.0", + "indexmap 1.9.3", + "stable_deref_trait", +] + [[package]] name = "gimli" version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator 0.3.0", + "indexmap 2.2.5", + "stable_deref_trait", +] + [[package]] name = "h2" version = "0.3.20" @@ -1035,32 +1551,37 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.6", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] [[package]] name = "hashlink" -version = "0.7.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.14.3", ] [[package]] @@ -1118,6 +1639,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "hiro-system-kit" +version = "0.1.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" +dependencies = [ + "ansi_term", + "atty", + "lazy_static", + "tokio", +] + [[package]] name = "hmac" version = "0.7.1" @@ -1169,6 +1701,15 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.9" @@ -1226,7 +1767,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1300,6 +1841,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "idna" version = "0.4.0" @@ -1310,6 +1857,26 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1322,12 +1889,22 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", ] [[package]] @@ -1362,7 +1939,16 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", "rustix", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", ] [[package]] @@ -1371,6 +1957,35 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "ittapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" +dependencies = [ + "cc", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -1511,7 +2126,7 @@ version = "0.82.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "125331201e3073707ac79c294c89021faa76c84da3a566a3749a2a93d295c98a" dependencies = [ - "ahash 0.8.3", + "ahash", "async-trait", "backoff", "derivative", @@ -1536,11 +2151,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" -version = "0.2.147" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libsecp256k1" @@ -1608,9 +2229,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.24.2" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" dependencies = [ "cc", "pkg-config", @@ -1634,9 +2255,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -1654,11 +2275,38 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memfd" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] [[package]] name = "memoffset" @@ -1669,6 +2317,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "memzero" version = "0.1.0" @@ -1692,13 +2349,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1720,9 +2377,25 @@ dependencies = [ "cc", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.15" @@ -1760,6 +2433,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "crc32fast", + "hashbrown 0.14.3", + "indexmap 2.2.5", + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -1801,7 +2486,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -1825,6 +2510,60 @@ dependencies = [ "num-traits", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256k1" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a64d160b891178fb9d43d1a58ddcafb6502daeb54d810e5e92a7c3c9bfacc07" +dependencies = [ + "bitvec", + "bs58 0.4.0", + "cc", + "hex", + "itertools", + "num-traits", + "primitive-types", + "proc-macro2", + "quote", + "rand_core 0.6.4", + "rustfmt-wrapper", + "serde", + "sha2 0.10.7", + "syn 2.0.53", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1845,25 +2584,31 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets", + "windows-targets 0.48.1", ] [[package]] name = "password-hash" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", "rand_core 0.6.4", "subtle 2.5.0", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pbkdf2" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", "hmac 0.12.1", @@ -1886,21 +2631,11 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -[[package]] -name = "pest" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" -dependencies = [ - "thiserror", - "ucd-trie", -] - [[package]] name = "pico-args" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" @@ -1919,14 +2654,14 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1934,12 +2669,66 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "polynomial" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27abb6e4638dcecc65a92b50d7f1d87dd6dea987ba71db987b6bf881f4877e9d" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pox-locking" +version = "2.4.0" +source = "git+https://github.com/stacks-network/stacks-core.git?branch=feat/clarity-wasm-next#5eb967cf4f58efcffe385c7a6fd2ed82e5f5da58" +dependencies = [ + "clarity", + "slog", + "stacks-common", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1960,6 +2749,27 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1984,17 +2794,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -2010,13 +2814,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radix_trie" version = "0.2.1" @@ -2104,6 +2914,26 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2122,6 +2952,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2134,33 +2973,61 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.9.1" +name = "regalloc2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", ] [[package]] -name = "regex-automata" -version = "0.3.3" +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.2", ] [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" @@ -2225,42 +3092,17 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "rstest" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2288c66aeafe3b2ed227c981f364f9968fa952ef0b30e84ada4486e7ee24d00a" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "rustc_version 0.4.0", - "syn 1.0.109", -] - -[[package]] -name = "rstest_reuse" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c6cfaae58c048728261723a72b80a0aa9f3768e9a7da3b302a24d262525219" -dependencies = [ - "quote", - "rustc_version 0.3.3", - "syn 1.0.109", -] - [[package]] name = "rusqlite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" +checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" dependencies = [ "bitflags 1.3.2", - "fallible-iterator", + "fallible-iterator 0.2.0", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", - "memchr", "serde_json", "smallvec", ] @@ -2272,22 +3114,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc_version" -version = "0.3.3" +name = "rustc-hex" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" @@ -2295,20 +3131,33 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.18", + "semver", +] + +[[package]] +name = "rustfmt-wrapper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" +dependencies = [ + "serde", + "tempfile", + "thiserror", + "toml 0.8.8", + "toolchain_find", ] [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2378,11 +3227,19 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schemars" version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#15fdd4711700114d57c090aad62516593bd4ca6d" dependencies = [ "dyn-clone", "schemars_derive", @@ -2393,8 +3250,7 @@ dependencies = [ [[package]] name = "schemars_derive" version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +source = "git+https://github.com/hirosystems/schemars.git?branch=feat-chainhook-fixes#15fdd4711700114d57c090aad62516593bd4ca6d" dependencies = [ "proc-macro2", "quote", @@ -2448,50 +3304,17 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser 0.10.2", -] - [[package]] name = "semver" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" -version = "1.0.171" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -2508,13 +3331,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -2530,16 +3353,25 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.5", "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_stacker" version = "0.1.10" @@ -2580,7 +3412,7 @@ version = "0.9.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5f51e3fdb5b9cdd1577e1cb7a733474191b1aca6a72c2e50913241632c1180" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -2609,24 +3441,9 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", + "syn 2.0.53", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sha2" version = "0.8.2" @@ -2674,24 +3491,21 @@ dependencies = [ [[package]] name = "sha3" -version = "0.9.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", + "digest 0.10.7", "keccak", - "opaque-debug 0.3.0", ] [[package]] -name = "sha3" -version = "0.10.8" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "digest 0.10.7", - "keccak", + "lazy_static", ] [[package]] @@ -2703,6 +3517,15 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "slab" version = "0.4.8" @@ -2712,6 +3535,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + [[package]] name = "slog" version = "2.7.0" @@ -2749,7 +3578,7 @@ dependencies = [ "serde", "serde_json", "slog", - "time 0.3.23", + "time", ] [[package]] @@ -2773,7 +3602,7 @@ dependencies = [ "slog", "term", "thread_local", - "time 0.3.23", + "time", ] [[package]] @@ -2792,12 +3621,44 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "stacker" version = "0.1.15" @@ -2814,16 +3675,17 @@ dependencies = [ [[package]] name = "stacks-common" version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c887baefb047fb7cd1c82d5a62e1deaca4b9ea9c701f965407db6c39a3fc902f" +source = "git+https://github.com/stacks-network/stacks-core.git?branch=feat/clarity-wasm-next#5eb967cf4f58efcffe385c7a6fd2ed82e5f5da58" dependencies = [ "chrono", - "curve25519-dalek", + "curve25519-dalek 2.0.0", "ed25519-dalek", + "hashbrown 0.14.3", "lazy_static", "libc", + "nix", "percent-encoding", - "rand 0.7.3", + "rand 0.8.5", "ripemd", "rusqlite", "secp256k1", @@ -2831,11 +3693,13 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.7", - "sha3 0.10.8", + "sha3", "slog", "slog-json", "slog-term", - "time 0.2.27", + "time", + "winapi", + "wsts", ] [[package]] @@ -2846,9 +3710,8 @@ dependencies = [ "clarinet-deployments", "clarinet-files", "clarity-repl", - "clarity-vm", "futures", - "hiro-system-kit", + "hiro-system-kit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "http-body", "hyper", "k8s-openapi", @@ -2862,16 +3725,15 @@ dependencies = [ "strum_macros 0.24.3", "test-case", "tokio", - "toml", + "toml 0.5.11", "tower", "tower-test", ] [[package]] name = "stacks-rpc-client" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12eef9ff174e8345b414abbfa5ed7e271e772d1e6b2c3eaa9f7f02209c87f48e" +version = "2.4.0" +source = "git+https://github.com/hirosystems/clarinet.git?rev=a5f9fea72230b893a7d1f90bdfda3a68aa48e908#a5f9fea72230b893a7d1f90bdfda3a68aa48e908" dependencies = [ "clarity-repl", "hmac 0.12.1", @@ -2886,62 +3748,10 @@ dependencies = [ ] [[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "str-buf" @@ -2949,6 +3759,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.23.0" @@ -3015,9 +3831,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -3030,6 +3846,31 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "term" version = "0.7.0" @@ -3078,22 +3919,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -3108,82 +3949,37 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.23" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ + "deranged", "itoa", "libc", + "num-conv", "num_threads", + "powerfmt", "serde", "time-core", - "time-macros 0.2.10", + "time-macros", ] [[package]] name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tiny-hderive" version = "0.3.0" @@ -3214,11 +4010,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -3227,9 +4022,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3244,13 +4039,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -3301,9 +4096,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -3324,6 +4119,64 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.2.5", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toolchain_find" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" +dependencies = [ + "home", + "once_cell", + "regex", + "semver", + "walkdir", +] + [[package]] name = "tower" version = "0.4.13" @@ -3348,7 +4201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ac8060a61f8758a61562f6fb53ba3cbe1ca906f001df2e53cccddcdbee91e7c" dependencies = [ "base64 0.21.4", - "bitflags 2.3.3", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", @@ -3409,7 +4262,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", ] [[package]] @@ -3419,6 +4272,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3443,10 +4326,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] -name = "ucd-trie" -version = "0.1.6" +name = "uint" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] [[package]] name = "unicode-bidi" @@ -3481,6 +4370,22 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle 2.5.0", +] + [[package]] name = "unsafe-libyaml" version = "0.2.9" @@ -3511,6 +4416,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3523,6 +4440,44 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "walrus" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7" +dependencies = [ + "anyhow", + "gimli 0.26.2", + "id-arena", + "leb128", + "log", + "walrus-macro", + "wasm-encoder 0.29.0", + "wasmparser 0.80.2", +] + +[[package]] +name = "walrus-macro" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "want" version = "0.3.1" @@ -3538,12 +4493,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3571,7 +4520,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -3605,7 +4554,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3616,6 +4565,354 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-encoder" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmparser" +version = "0.80.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b" + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.2.5", + "semver", +] + +[[package]] +name = "wasmtime" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642e12d108e800215263e3b95972977f473957923103029d7d617db701d67ba4" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "fxprof-processed-profile", + "indexmap 2.2.5", + "libc", + "log", + "object 0.32.2", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "serde_derive", + "serde_json", + "target-lexicon", + "wasm-encoder 0.36.2", + "wasmparser 0.116.1", + "wasmtime-cache", + "wasmtime-component-macro", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wat", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beada8bb15df52503de0a4c58de4357bfd2f96d9a44a6e547bad11efdd988b47" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba5bf44d044d25892c03fb3534373936ee204141ff92bac8297787ac7f22318" +dependencies = [ + "anyhow", + "base64 0.21.4", + "bincode", + "directories-next", + "log", + "rustix", + "serde", + "serde_derive", + "sha2 0.10.7", + "toml 0.5.11", + "windows-sys 0.48.0", + "zstd", +] + +[[package]] +name = "wasmtime-component-macro" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccba556991465cca68d5a54769684bcf489fb532059da55105f851642d52c1" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.53", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-component-util" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05492a177a6006cb73f034d6e9a6fad6da55b23c4398835cb0012b5fa51ecf67" + +[[package]] +name = "wasmtime-cranelift" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2e7532f1d6adbcc57e69bb6a7c503f0859076d07a9b4b6aabe8021ff8a05fd" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.28.1", + "log", + "object 0.32.2", + "target-lexicon", + "thiserror", + "wasmparser 0.116.1", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c98d5378a856cbf058d36278627dfabf0ed68a888142958c7ae8e6af507dafa" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli 0.28.1", + "object 0.32.2", + "target-lexicon", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d33a9f421da810a070cd56add9bc51f852bd66afbb8b920489d6242f15b70e" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.28.1", + "indexmap 2.2.5", + "log", + "object 0.32.2", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasmparser 0.116.1", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404741f4c6d7f4e043be2e8b466406a2aee289ccdba22bf9eba6399921121b97" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d0994a86d6dca5f7d9740d7f2bd0568be06d2014a550361dc1c397d289d81ef" +dependencies = [ + "addr2line 0.21.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.28.1", + "ittapi", + "log", + "object 0.32.2", + "rustc-demangle", + "rustix", + "serde", + "serde_derive", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0c4b74e606d1462d648631d5bc328e3d5b14e7f9d3ff93bc6db062fb8c5cd8" +dependencies = [ + "object 0.32.2", + "once_cell", + "rustix", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3090a69ba1476979e090aa7ed4bc759178bafdb65b22f98b9ba24fc6e7e578d5" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b993ac8380385ed67bf71b51b9553edcf1ab0801b78a805a067de581b9a3e88a" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 2.2.5", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.9.0", + "paste", + "rand 0.8.5", + "rustix", + "sptr", + "wasm-encoder 0.36.2", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-debug", + "wasmtime-versioned-export-macros", + "wasmtime-wmemcheck", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-types" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5778112fcab2dc3d4371f4203ab8facf0c453dd94312b0a88dd662955e64e0" +dependencies = [ + "cranelift-entity", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.116.1", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50f51f8d79bfd2aa8e9d9a0ae7c2d02b45fe412e62ff1b87c0c81b07c738231" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b804dfd3d0c0d6d37aa21026fe7772ba1a769c89ee4f5c4f13b82d91d75216f" +dependencies = [ + "anyhow", + "heck 0.4.1", + "indexmap 2.2.5", + "wit-parser", +] + +[[package]] +name = "wasmtime-wmemcheck" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6060bc082cc32d9a45587c7640e29e3c7b89ada82677ac25d87850aaccb368" + +[[package]] +name = "wast" +version = "70.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee4bc54bbe1c6924160b9f75e374a1d07532e7580eb632c0ee6cdd109bb217e" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.39.0", +] + +[[package]] +name = "wat" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0dce8cdc288c717cf01e461a1e451a7b8445d53451123536ba576e423a101a" +dependencies = [ + "wast", +] + [[package]] name = "web-sys" version = "0.3.64" @@ -3661,6 +4958,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3673,7 +4979,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -3682,7 +4988,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -3691,13 +5006,28 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3706,42 +5036,93 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -3751,6 +5132,54 @@ dependencies = [ "winapi", ] +[[package]] +name = "wit-parser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.2.5", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "wsts" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467aa8e40ed0277d19922fd0e7357c16552cb900e5138f61a48ac23c4b7878e0" +dependencies = [ + "aes-gcm", + "bs58 0.5.0", + "hashbrown 0.14.3", + "hex", + "num-traits", + "p256k1", + "polynomial", + "primitive-types", + "rand_core 0.6.4", + "serde", + "sha2 0.10.7", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -3760,8 +5189,57 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + [[package]] name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index cb663ca..79f7dc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,9 @@ repository = "https://github.com/hirosystems/stacks-devnet-api" kube = { version="0.82.2", features = ["client", "runtime"] } k8s-openapi = { version = "0.18.0", features = ["v1_25"] } futures = "0.3.28" -tokio = { version = "1.27.0", features = ["full"] } +tokio = { version = "1.35.1", features = ["full"] } serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.96" +serde_json = "1.0.114" serde_yaml = "0.8.23" hyper = { version = "0.14", features = ["full"] } tower = "0.4.13" @@ -25,11 +25,17 @@ http-body = "0.4.5" hiro-system-kit = {version = "0.1.0", features = ["log"]} strum_macros = "0.24.3" strum = "0.24.1" -clarity-vm = "2.1.1" -clarity-repl = "1.8.0" -clarinet-files = {version = "1.0.3" } -chainhook-types = "1.0" -clarinet-deployments = {version = "1.0.3" } +# clarity-repl = "1.8.0" +# clarity-repl = {version = "2.2.0", path = "../clarinet/components/clarity-repl" } +clarity-repl = {version = "2.3.1", git = "https://github.com/hirosystems/clarinet.git", rev="a5f9fea72230b893a7d1f90bdfda3a68aa48e908" } +# clarinet-files = {version = "1.0.3" } +# clarinet-files = {version = "2.2.0", path = "../clarinet/components/clarinet-files" } +clarinet-files = {version = "2.3.1", git = "https://github.com/hirosystems/clarinet.git", rev="a5f9fea72230b893a7d1f90bdfda3a68aa48e908" } +# clarinet-deployments = {version = "1.0.3" } +# clarinet-deployments = {version = "2.2.0", path = "../clarinet/components/clarinet-deployments" } +clarinet-deployments = {version = "2.3.1", git = "https://github.com/hirosystems/clarinet.git", rev="a5f9fea72230b893a7d1f90bdfda3a68aa48e908" } +# chainhook-types = "1.0" +chainhook-types = { version = "1.3", git = "https://github.com/hirosystems/chainhook.git", branch="chore/update-clarinet-and-clarity" } toml = "0.5.9" [dev-dependencies] diff --git a/Dockerfile b/Dockerfile index d2278f1..f046520 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,10 @@ FROM rust:bullseye as builder -RUN apt update && apt install -y ca-certificates pkg-config libssl-dev +RUN apt update && apt install -y ca-certificates pkg-config libssl-dev libclang-dev WORKDIR /src COPY . /src RUN mkdir /out +RUN rustup component add rustfmt RUN cargo build --release --manifest-path ./Cargo.toml RUN cp target/release/stacks-devnet-api /out diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..292fe49 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/src/config.rs b/src/config.rs index efea780..a1a7017 100644 --- a/src/config.rs +++ b/src/config.rs @@ -30,7 +30,7 @@ pub struct StacksDevnetConfig { disable_bitcoin_explorer: Option, // todo: currently unused disable_stacks_explorer: Option, // todo: currently unused deployment_plan: DeploymentSpecification, - network_manifest: NetworkManifest, + pub network_manifest: NetworkManifest, project_manifest: ProjectManifest, } impl StacksDevnetConfig { diff --git a/src/lib.rs b/src/lib.rs index 400de22..474cd54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ use chainhook_types::StacksNetwork; -use clarinet_files::compute_addresses; +use clarinet_files::{ + compute_addresses, DEFAULT_STACKS_API_IMAGE_NAKA, DEFAULT_STACKS_NODE_IMAGE_NAKA, +}; use futures::future::try_join4; use hiro_system_kit::{slog, Logger}; use hyper::{body::Bytes, Body, Client as HttpClient, Request, Response, Uri}; @@ -19,7 +21,7 @@ use resources::{ deployment::StacksDevnetDeployment, pvc::StacksDevnetPvc, service::{get_service_port, ServicePort}, - stateful_set::StacksDevnetStatefulSet, + stateful_set::{SignerIdx, StacksDevnetStatefulSet}, StacksDevnetResource, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -45,7 +47,6 @@ use crate::resources::service::{get_service_url, StacksDevnetService}; const COMPONENT_SELECTOR: &str = "app.kubernetes.io/component"; const USER_SELECTOR: &str = "app.kubernetes.io/instance"; const NAME_SELECTOR: &str = "app.kubernetes.io/name"; - #[derive(Clone, Debug)] pub struct DevNetError { pub message: String, @@ -222,6 +223,21 @@ impl StacksDevnetApiK8sManager { sleep(Duration::from_secs(5)); self.deploy_stacks_blockchain(&config).await?; + if config.devnet_config.use_nakamoto { + self.deploy_stacks_signer( + &config, + SignerIdx::Signer0, + "7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801", + ) + .await?; + + self.deploy_stacks_signer( + &config, + SignerIdx::Signer1, + "530d9f61984c888536871c6573073bdfc0058896dc1adfe9a6a10dfacadc209101", + ) + .await?; + } if !config.disable_stacks_api { self.deploy_stacks_blockchain_api(&config).await?; @@ -251,7 +267,25 @@ impl StacksDevnetApiK8sManager { let stateful_sets: Vec = StacksDevnetStatefulSet::iter() .map(|p| p.to_string()) .collect(); + for stateful_set in stateful_sets { + // todo: remove once Clarinet removes `use_nakamoto` + // becuase the devnet api is stateless, we don't have access to the config + // that was used to create the devnet originally. So, we don't know if this + // devnet has the nakamoto assets deployed or not. + // So when deleting the signer assets, just skip instead of erroring if they don't exist + if &stateful_set.as_str() == &"stacks-signer-0" + || &stateful_set.as_str() == &"stacks-signer-1" + { + match self + .check_resource_exists::(namespace, &stateful_set) + .await + { + Ok(true) | Err(_) => {} + Ok(false) => continue, + } + }; + if let Err(e) = self .delete_resource::(namespace, &stateful_set) .await @@ -264,6 +298,19 @@ impl StacksDevnetApiK8sManager { .map(|c| c.to_string()) .collect(); for configmap in configmaps { + // todo: remove once Clarinet removes `use_nakamoto` + if &configmap.as_str() == &"stacks-signer-0" + || &configmap.as_str() == &"stacks-signer-1" + { + match self + .check_resource_exists::(namespace, &configmap) + .await + { + Ok(true) | Err(_) => {} + Ok(false) => continue, + } + }; + if let Err(e) = self .delete_resource::(namespace, &configmap) .await @@ -275,6 +322,18 @@ impl StacksDevnetApiK8sManager { let services: Vec = StacksDevnetService::iter().map(|s| s.to_string()).collect(); for service in services { + // todo: remove once Clarinet removes `use_nakamoto` + if &service.as_str() == &"stacks-signer-0" + || &service.as_str() == &"stacks-signer-1" + { + match self + .check_resource_exists::(namespace, &service) + .await + { + Ok(true) | Err(_) => {} + Ok(false) => continue, + } + }; if let Err(e) = self.delete_resource::(namespace, &service).await { errors.push(e); } @@ -283,6 +342,18 @@ impl StacksDevnetApiK8sManager { let pvcs: Vec = StacksDevnetPvc::iter().map(|pvc| pvc.to_string()).collect(); for pvc in pvcs { + // todo: remove once Clarinet removes `use_nakamoto` + if &pvc.as_str() == &"stacks-signer-0" || &pvc.as_str() == &"stacks-signer-1" { + match self + .check_resource_exists_by_label::( + namespace, &pvc, user_id, + ) + .await + { + Ok(true) | Err(_) => {} + Ok(false) => continue, + } + }; if let Err(e) = self .delete_resource_by_label::(namespace, &pvc, user_id) .await @@ -384,8 +455,13 @@ impl StacksDevnetApiK8sManager { return Ok(true); } } - - for stateful_set in StacksDevnetStatefulSet::iter() { + // todo: we are not checking for assets that are only deployed if use_nakamoto is true (remove filter once `use_nakamoto` is no longer around) + for stateful_set in StacksDevnetStatefulSet::iter().filter(|sts| match sts { + StacksDevnetStatefulSet::StacksSigner0 | StacksDevnetStatefulSet::StacksSigner1 => { + false + } + _ => true, + }) { if self .check_resource_exists::(namespace, &stateful_set.to_string()) .await? @@ -393,8 +469,11 @@ impl StacksDevnetApiK8sManager { return Ok(true); } } - - for pod in StacksDevnetPod::iter() { + // todo: remove filter when nakamoto is stable + for pod in StacksDevnetPod::iter().filter(|pod| match pod { + StacksDevnetPod::StacksSigner0 | StacksDevnetPod::StacksSigner1 => false, + _ => true, + }) { if self .check_resource_exists_by_label::(namespace, &pod.to_string(), user_id) .await? @@ -403,7 +482,11 @@ impl StacksDevnetApiK8sManager { } } - for configmap in StacksDevnetConfigmap::iter() { + // todo: remove filter when nakamoto is stable + for configmap in StacksDevnetConfigmap::iter().filter(|c| match c { + StacksDevnetConfigmap::StacksSigner0 | StacksDevnetConfigmap::StacksSigner1 => false, + _ => true, + }) { if self .check_resource_exists::(namespace, &configmap.to_string()) .await? @@ -412,7 +495,11 @@ impl StacksDevnetApiK8sManager { } } - for service in StacksDevnetService::iter() { + // todo: remove filter when nakamoto is stable + for service in StacksDevnetService::iter().filter(|svc| match svc { + StacksDevnetService::StacksSigner0 | StacksDevnetService::StacksSigner1 => false, + _ => true, + }) { if self .check_resource_exists::(namespace, &service.to_string()) .await? @@ -421,7 +508,11 @@ impl StacksDevnetApiK8sManager { } } - for pvc in StacksDevnetPvc::iter() { + // todo: remove filter when nakamoto is stable + for pvc in StacksDevnetPvc::iter().filter(|pvc| match pvc { + StacksDevnetPvc::StacksSigner0 | StacksDevnetPvc::StacksSigner1 => false, + _ => true, + }) { if self .check_resource_exists_by_label::( namespace, @@ -455,8 +546,13 @@ impl StacksDevnetApiK8sManager { return Ok(false); } } - - for stateful_set in StacksDevnetStatefulSet::iter() { + // todo: we are not checking for assets that are only deployed if use_nakamoto is true (remove filter once `use_nakamoto` is no longer around) + for stateful_set in StacksDevnetStatefulSet::iter().filter(|sts| match sts { + StacksDevnetStatefulSet::StacksSigner0 | StacksDevnetStatefulSet::StacksSigner1 => { + false + } + _ => true, + }) { if !self .check_resource_exists::(namespace, &stateful_set.to_string()) .await? @@ -465,7 +561,11 @@ impl StacksDevnetApiK8sManager { } } - for configmap in StacksDevnetConfigmap::iter() { + // todo: remove filter when nakamoto is stable + for configmap in StacksDevnetConfigmap::iter().filter(|c| match c { + StacksDevnetConfigmap::StacksSigner0 | StacksDevnetConfigmap::StacksSigner1 => false, + _ => true, + }) { if !self .check_resource_exists::(namespace, &configmap.to_string()) .await? @@ -474,7 +574,11 @@ impl StacksDevnetApiK8sManager { } } - for service in StacksDevnetService::iter() { + // todo: remove filter when nakamoto is stable + for service in StacksDevnetService::iter().filter(|svc| match svc { + StacksDevnetService::StacksSigner0 | StacksDevnetService::StacksSigner1 => false, + _ => true, + }) { if !self .check_resource_exists::(namespace, &service.to_string()) .await? @@ -939,16 +1043,19 @@ impl StacksDevnetApiK8sManager { async fn deploy_deployment( &self, - deployment: StacksDevnetDeployment, + deployment_type: StacksDevnetDeployment, namespace: &str, user_id: &str, + use_nakamoto: bool, ) -> Result<(), DevNetError> { + let deployment_type_moved = deployment_type.clone(); let mut deployment: Deployment = - self.get_resource_from_file(StacksDevnetResource::Deployment(deployment))?; + self.get_resource_from_file(StacksDevnetResource::Deployment(deployment_type_moved))?; let key = "app.kubernetes.io/instance".to_string(); let user_id = user_id.to_owned(); + // set deployment metadata labels to include user id if let Some(mut labels) = deployment.clone().metadata.labels { if let Some(label) = labels.get_mut(&key) { *label = user_id.clone(); @@ -958,6 +1065,7 @@ impl StacksDevnetApiK8sManager { deployment.metadata.labels = Some(labels); } + // set deployment spec with user-specific data if let Some(mut spec) = deployment.clone().spec { if let Some(mut match_labels) = spec.selector.match_labels { if let Some(match_label) = match_labels.get_mut(&key) { @@ -968,7 +1076,8 @@ impl StacksDevnetApiK8sManager { spec.selector.match_labels = Some(match_labels); } - if let Some(mut metadata) = spec.template.metadata { + let mut template = spec.template; + if let Some(mut metadata) = template.metadata { if let Some(mut labels) = metadata.labels { if let Some(label) = labels.get_mut(&key) { *label = user_id.clone(); @@ -977,8 +1086,40 @@ impl StacksDevnetApiK8sManager { } metadata.labels = Some(labels); } - spec.template.metadata = Some(metadata); + template.metadata = Some(metadata); + } + + if use_nakamoto { + match &deployment_type { + StacksDevnetDeployment::StacksBlockchain => { + if let Some(mut pod_spec) = template.spec { + for container in &mut pod_spec.containers { + if container.name == "stacks-blockchain" { + container.image = + Some(DEFAULT_STACKS_NODE_IMAGE_NAKA.to_owned()); + } + } + template.spec = Some(pod_spec); + } else { + let msg = format!( + "failed to set nakamoto image for RESOURCE: deployment, NAME: {}, NAMESPACE: {}", + deployment_type, namespace + ); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + return Err(DevNetError { + message: msg, + code: 500, + }); + } + } + // note: initially the plan was to conditionally upgrade the version of clarinet we're using + // however, when the platform sends us a config from `clarinet devnet package`, it will contain + // new fields that the devnet needs to handle; if we have multiple clarinet versions here, + // we need multiple clarinet versions at the platform level, and multiple config types + StacksDevnetDeployment::BitcoindNode => {} + } } + spec.template = template; deployment.spec = Some(spec); } @@ -990,12 +1131,14 @@ impl StacksDevnetApiK8sManager { async fn deploy_stateful_set( &self, - stateful_set: StacksDevnetStatefulSet, + stateful_set_type: StacksDevnetStatefulSet, namespace: &str, user_id: &str, + use_nakamoto: bool, ) -> Result<(), DevNetError> { - let mut stateful_set: StatefulSet = - self.get_resource_from_file(StacksDevnetResource::StatefulSet(stateful_set))?; + let stateful_set_type_moved = stateful_set_type.clone(); + let mut stateful_set: StatefulSet = self + .get_resource_from_file(StacksDevnetResource::StatefulSet(stateful_set_type_moved))?; let key = "app.kubernetes.io/instance".to_string(); let user_id = user_id.to_owned(); @@ -1018,7 +1161,8 @@ impl StacksDevnetApiK8sManager { spec.selector.match_labels = Some(match_labels); } - if let Some(mut metadata) = spec.template.metadata { + let mut template = spec.template; + if let Some(mut metadata) = template.metadata { if let Some(mut labels) = metadata.labels { if let Some(label) = labels.get_mut(&key) { *label = user_id.clone(); @@ -1027,9 +1171,34 @@ impl StacksDevnetApiK8sManager { } metadata.labels = Some(labels); } - spec.template.metadata = Some(metadata); + template.metadata = Some(metadata); } + if use_nakamoto { + match &stateful_set_type { + StacksDevnetStatefulSet::StacksBlockchainApi => { + if let Some(mut pod_spec) = template.spec { + for container in &mut pod_spec.containers { + if container.name == "stacks-blockchain-api" { + container.image = + Some(DEFAULT_STACKS_API_IMAGE_NAKA.to_owned()); + } + } + template.spec = Some(pod_spec); + } else { + let msg = format!("failed to set nakamoto image for RESOURCE: deployment, NAME: {}, NAMESPACE: {}", stateful_set_type, namespace); + self.ctx.try_log(|logger| slog::error!(logger, "{}", msg)); + return Err(DevNetError { + message: msg, + code: 500, + }); + } + } + StacksDevnetStatefulSet::StacksSigner0 + | StacksDevnetStatefulSet::StacksSigner1 => {} + } + } + spec.template = template; stateful_set.spec = Some(spec); } @@ -1071,6 +1240,7 @@ impl StacksDevnetApiK8sManager { service.spec = Some(spec); } + service.metadata.namespace = Some(namespace.to_owned()); self.deploy_resource(namespace, service, "service").await } @@ -1185,8 +1355,13 @@ impl StacksDevnetApiK8sManager { ) .await?; - self.deploy_deployment(StacksDevnetDeployment::BitcoindNode, &namespace, &user_id) - .await?; + self.deploy_deployment( + StacksDevnetDeployment::BitcoindNode, + &namespace, + &user_id, + config.devnet_config.use_nakamoto, + ) + .await?; self.deploy_service(StacksDevnetService::BitcoindNode, namespace, &user_id) .await?; @@ -1218,12 +1393,15 @@ impl StacksDevnetApiK8sManager { working_dir = "/devnet" rpc_bind = "0.0.0.0:{}" p2p_bind = "0.0.0.0:{}" + data_url = "http://127.0.0.1:{}" + p2p_address = "127.0.0.1:{}" miner = true seed = "{}" local_peer_seed = "{}" pox_sync_sample_secs = 0 wait_time_for_blocks = 0 - wait_time_for_microblocks = {} + wait_time_for_microblocks = 0 + mine_microblocks = false microblock_frequency = 1000 [connection_options] @@ -1234,18 +1412,24 @@ impl StacksDevnetApiK8sManager { disable_inbound_handshakes = true disable_inbound_walks = true public_ip_address = "1.1.1.1:1234" + block_proposal_token = "12345" [miner] + min_tx_fee = 1 first_attempt_time_ms = {} - subsequent_attempt_time_ms = {} + second_attempt_time_ms = {} block_reward_recipient = "{}" - # microblock_attempt_time_ms = 15000 + wait_for_block_download = false + microblock_attempt_time_ms = 10 + self_signing_seed = 1 + mining_key = "19ec1c3e31d139c989a23a27eac60d1abfad5277d3ae9604242514c738258efa01" "#, get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(), get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::P2P).unwrap(), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::P2P).unwrap(), stacks_miner_secret_key_hex, stacks_miner_secret_key_hex, - devnet_config.stacks_node_wait_time_for_microblocks, devnet_config.stacks_node_first_attempt_time_ms, devnet_config.stacks_node_subsequent_attempt_time_ms, miner_coinbase_recipient @@ -1301,22 +1485,69 @@ impl StacksDevnetApiK8sManager { .unwrap(), )); + if devnet_config.use_nakamoto { + for signer_idx in SignerIdx::iter() { + let (url, port) = match signer_idx { + SignerIdx::Signer0 => ( + get_service_url(&namespace, StacksDevnetService::StacksSigner0), + get_service_port( + StacksDevnetService::StacksSigner0, + ServicePort::Event, + ) + .unwrap(), + ), + SignerIdx::Signer1 => ( + get_service_url(&namespace, StacksDevnetService::StacksSigner1), + get_service_port( + StacksDevnetService::StacksSigner1, + ServicePort::Event, + ) + .unwrap(), + ), + }; + + stacks_conf.push_str(&format!( + r#" + # Add stacks-signer-{} as an event observer + [[events_observer]] + endpoint = "{}:{}" + retry_count = 255 + include_data_events = false + events_keys = ["stackerdb", "block_proposal", "burn_blocks"] + "#, + signer_idx.to_string(), + url, + port, + )); + } + } + stacks_conf.push_str(&format!( r#" [burnchain] chain = "bitcoin" - mode = "krypton" + mode = "{}" + magic_bytes = "T3" + pox_prepare_length = 4 + pox_reward_length = 10 + burn_fee_cap = 20_000 poll_time_secs = 1 timeout = 30 peer_host = "{}" rpc_ssl = false - wallet_name = "devnet" + wallet_name = "{}" username = "{}" password = "{}" rpc_port = {} peer_port = {} "#, + if devnet_config.use_nakamoto { + "nakamoto-neon" + } else { + "krypton" + }, bitcoind_chain_coordinator_host, + devnet_config.miner_wallet_name, devnet_config.bitcoin_node_username, devnet_config.bitcoin_node_password, chain_coordinator_ingestion_port, @@ -1325,8 +1556,6 @@ impl StacksDevnetApiK8sManager { stacks_conf.push_str(&format!( r#" - pox_2_activation = {} - [[burnchain.epochs]] epoch_name = "1.0" start_height = 0 @@ -1343,16 +1572,38 @@ impl StacksDevnetApiK8sManager { epoch_name = "2.1" start_height = {} - # [[burnchain.epochs]] - # epoch_name = "2.2" - # start_height = {} + [[burnchain.epochs]] + epoch_name = "2.2" + start_height = {} + + [[burnchain.epochs]] + epoch_name = "2.3" + start_height = {} + + [[burnchain.epochs]] + epoch_name = "2.4" + start_height = {} "#, - devnet_config.pox_2_activation, devnet_config.epoch_2_0, devnet_config.epoch_2_05, devnet_config.epoch_2_1, devnet_config.epoch_2_2, + devnet_config.epoch_2_3, + devnet_config.epoch_2_4 )); + if devnet_config.use_nakamoto { + stacks_conf.push_str(&format!( + r#" + [[burnchain.epochs]] + epoch_name = "2.5" + start_height = {} + [[burnchain.epochs]] + epoch_name = "3.0" + start_height = {} + "#, + devnet_config.epoch_2_5, devnet_config.epoch_3_0, + )); + } stacks_conf }; @@ -1367,6 +1618,7 @@ impl StacksDevnetApiK8sManager { StacksDevnetDeployment::StacksBlockchain, &namespace, &user_id, + config.devnet_config.use_nakamoto, ) .await?; @@ -1434,6 +1686,7 @@ impl StacksDevnetApiK8sManager { StacksDevnetStatefulSet::StacksBlockchainApi, &namespace, user_id, + config.devnet_config.use_nakamoto, ) .await?; @@ -1447,6 +1700,72 @@ impl StacksDevnetApiK8sManager { Ok(()) } + async fn deploy_stacks_signer( + &self, + config: &ValidatedStacksDevnetConfig, + signer_idx: SignerIdx, + signer_key: &str, + ) -> Result<(), DevNetError> { + let namespace = &config.namespace; + let user_id = &config.user_id; + + let signer_port = match signer_idx { + SignerIdx::Signer0 => { + get_service_port(StacksDevnetService::StacksSigner0, ServicePort::Event).unwrap() + } + SignerIdx::Signer1 => { + get_service_port(StacksDevnetService::StacksSigner1, ServicePort::Event).unwrap() + } + }; + + let configmap = match signer_idx { + SignerIdx::Signer0 => StacksDevnetConfigmap::StacksSigner0, + SignerIdx::Signer1 => StacksDevnetConfigmap::StacksSigner1, + }; + + let sts = match signer_idx { + SignerIdx::Signer0 => StacksDevnetStatefulSet::StacksSigner0, + SignerIdx::Signer1 => StacksDevnetStatefulSet::StacksSigner1, + }; + + let service = match signer_idx { + SignerIdx::Signer0 => StacksDevnetService::StacksSigner0, + SignerIdx::Signer1 => StacksDevnetService::StacksSigner1, + }; + + // configmap env vars for api conatainer + let signer_conf = format!( + r#" + stacks_private_key = "{}" + node_host = "{}:{}" + # must be added as event_observer in node config: + endpoint = "0.0.0.0:{}" + network = "testnet" + auth_password = "12345" + db_path = "/chainstate/stacks-signer-{}.sqlite" + "#, + signer_key, + get_service_url(&namespace, StacksDevnetService::StacksBlockchain), + get_service_port(StacksDevnetService::StacksBlockchain, ServicePort::RPC).unwrap(), + signer_port, + signer_idx.to_string() + ); + + self.deploy_configmap( + configmap, + &namespace, + Some(vec![("Signer.toml".into(), signer_conf)]), + ) + .await?; + + self.deploy_stateful_set(sts, &namespace, user_id, config.devnet_config.use_nakamoto) + .await?; + + self.deploy_service(service, &namespace, &user_id).await?; + + Ok(()) + } + async fn delete_resource>( &self, namespace: &str, diff --git a/src/resources/configmap.rs b/src/resources/configmap.rs index 773c172..9cfec10 100644 --- a/src/resources/configmap.rs +++ b/src/resources/configmap.rs @@ -7,6 +7,8 @@ pub enum StacksDevnetConfigmap { StacksBlockchain, StacksBlockchainApi, StacksBlockchainApiPg, + StacksSigner0, + StacksSigner1, DeploymentPlan, Devnet, ProjectDir, @@ -20,6 +22,8 @@ impl fmt::Display for StacksDevnetConfigmap { StacksDevnetConfigmap::StacksBlockchain => write!(f, "stacks-blockchain"), StacksDevnetConfigmap::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), StacksDevnetConfigmap::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api-pg"), + StacksDevnetConfigmap::StacksSigner0 => write!(f, "stacks-signer-0"), + StacksDevnetConfigmap::StacksSigner1 => write!(f, "stacks-signer-1"), StacksDevnetConfigmap::DeploymentPlan => write!(f, "deployment-plan"), StacksDevnetConfigmap::Devnet => write!(f, "devnet"), StacksDevnetConfigmap::ProjectDir => write!(f, "project-dir"), diff --git a/src/resources/deployment.rs b/src/resources/deployment.rs index 8635cd6..6eed220 100644 --- a/src/resources/deployment.rs +++ b/src/resources/deployment.rs @@ -1,7 +1,7 @@ use std::fmt; use strum_macros::EnumIter; -#[derive(EnumIter, Debug)] +#[derive(EnumIter, Debug, Clone)] pub enum StacksDevnetDeployment { BitcoindNode, StacksBlockchain, diff --git a/src/resources/pod.rs b/src/resources/pod.rs index 4270a79..2de33e1 100644 --- a/src/resources/pod.rs +++ b/src/resources/pod.rs @@ -6,6 +6,8 @@ pub enum StacksDevnetPod { BitcoindNode, StacksBlockchain, StacksBlockchainApi, + StacksSigner0, + StacksSigner1, } impl fmt::Display for StacksDevnetPod { @@ -14,6 +16,8 @@ impl fmt::Display for StacksDevnetPod { StacksDevnetPod::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), StacksDevnetPod::StacksBlockchain => write!(f, "stacks-blockchain"), StacksDevnetPod::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + StacksDevnetPod::StacksSigner0 => write!(f, "stacks-signer-0"), + StacksDevnetPod::StacksSigner1 => write!(f, "stacks-signer-1"), } } } diff --git a/src/resources/pvc.rs b/src/resources/pvc.rs index 58404b4..fe70cf3 100644 --- a/src/resources/pvc.rs +++ b/src/resources/pvc.rs @@ -4,12 +4,16 @@ use strum_macros::EnumIter; #[derive(EnumIter, Debug)] pub enum StacksDevnetPvc { StacksBlockchainApiPg, + StacksSigner0, + StacksSigner1, } impl fmt::Display for StacksDevnetPvc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StacksDevnetPvc::StacksBlockchainApiPg => write!(f, "stacks-blockchain-api"), + StacksDevnetPvc::StacksSigner0 => write!(f, "stacks-signer-0"), + StacksDevnetPvc::StacksSigner1 => write!(f, "stacks-signer-1"), } } } diff --git a/src/resources/service.rs b/src/resources/service.rs index 852e125..1a1d6a2 100644 --- a/src/resources/service.rs +++ b/src/resources/service.rs @@ -6,6 +6,8 @@ pub enum StacksDevnetService { BitcoindNode, StacksBlockchain, StacksBlockchainApi, + StacksSigner0, + StacksSigner1, } pub enum ServicePort { @@ -24,6 +26,8 @@ impl fmt::Display for StacksDevnetService { StacksDevnetService::BitcoindNode => write!(f, "bitcoind-chain-coordinator"), StacksDevnetService::StacksBlockchain => write!(f, "stacks-blockchain"), StacksDevnetService::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + StacksDevnetService::StacksSigner0 => write!(f, "stacks-signer-0"), + StacksDevnetService::StacksSigner1 => write!(f, "stacks-signer-1"), } } } @@ -39,6 +43,10 @@ pub fn get_service_port(service: StacksDevnetService, port_type: ServicePort) -> (StacksDevnetService::StacksBlockchainApi, ServicePort::API) => Some("3999".into()), (StacksDevnetService::StacksBlockchainApi, ServicePort::Event) => Some("3700".into()), (StacksDevnetService::StacksBlockchainApi, ServicePort::DB) => Some("5432".into()), + ( + StacksDevnetService::StacksSigner0 | StacksDevnetService::StacksSigner1, + ServicePort::Event, + ) => Some("30001".into()), (_, _) => None, } } @@ -49,6 +57,7 @@ pub fn get_user_facing_port(service: StacksDevnetService) -> Option { get_service_port(service, ServicePort::RPC) } StacksDevnetService::StacksBlockchainApi => get_service_port(service, ServicePort::API), + StacksDevnetService::StacksSigner0 | StacksDevnetService::StacksSigner1 => None, } } @@ -61,6 +70,8 @@ pub fn get_service_from_path_part(path_part: &str) -> Option Some(StacksDevnetService::BitcoindNode), "stacks-blockchain" => Some(StacksDevnetService::StacksBlockchain), "stacks-blockchain-api" => Some(StacksDevnetService::StacksBlockchainApi), + "stacks-signer-0" => Some(StacksDevnetService::StacksSigner0), + "stacks-signer-1" => Some(StacksDevnetService::StacksSigner1), _ => None, } } diff --git a/src/resources/stateful_set.rs b/src/resources/stateful_set.rs index c843604..13bdf0a 100644 --- a/src/resources/stateful_set.rs +++ b/src/resources/stateful_set.rs @@ -1,15 +1,34 @@ use std::fmt; use strum_macros::EnumIter; -#[derive(EnumIter, Debug)] +#[derive(EnumIter, Debug, Clone)] pub enum StacksDevnetStatefulSet { StacksBlockchainApi, + StacksSigner0, + StacksSigner1, } impl fmt::Display for StacksDevnetStatefulSet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StacksDevnetStatefulSet::StacksBlockchainApi => write!(f, "stacks-blockchain-api"), + StacksDevnetStatefulSet::StacksSigner0 => write!(f, "stacks-signer-0"), + StacksDevnetStatefulSet::StacksSigner1 => write!(f, "stacks-signer-1"), + } + } +} + +#[derive(EnumIter, Debug, Clone)] +pub enum SignerIdx { + Signer0, + Signer1, +} + +impl SignerIdx { + pub fn to_string(&self) -> String { + match &self { + SignerIdx::Signer0 => String::from("0"), + SignerIdx::Signer1 => String::from("1"), } } } diff --git a/src/resources/tests.rs b/src/resources/tests.rs index 38a6b7d..2ffe755 100644 --- a/src/resources/tests.rs +++ b/src/resources/tests.rs @@ -10,6 +10,8 @@ use test_case::test_case; #[test_case(StacksDevnetConfigmap::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] #[test_case(StacksDevnetConfigmap::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] #[test_case(StacksDevnetConfigmap::StacksBlockchainApiPg => is equal_to "stacks-blockchain-api-pg".to_string(); "for StacksBlockchainApiPg")] +#[test_case(StacksDevnetConfigmap::StacksSigner0 => is equal_to "stacks-signer-0".to_string(); "for StacksSigner0")] +#[test_case(StacksDevnetConfigmap::StacksSigner1 => is equal_to "stacks-signer-1".to_string(); "for StacksSigner1")] #[test_case(StacksDevnetConfigmap::DeploymentPlan => is equal_to "deployment-plan".to_string(); "for DeploymentPlan")] #[test_case(StacksDevnetConfigmap::Devnet => is equal_to "devnet".to_string(); "for Devnet")] #[test_case(StacksDevnetConfigmap::ProjectDir => is equal_to "project-dir".to_string(); "for ProjectDir")] @@ -32,6 +34,8 @@ fn it_prints_correct_name_for_deployment(deployment: StacksDevnetDeployment) -> } #[test_case(StacksDevnetStatefulSet::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] +#[test_case(StacksDevnetStatefulSet::StacksSigner0 => is equal_to "stacks-signer-0".to_string(); "for StacksSigner0")] +#[test_case(StacksDevnetStatefulSet::StacksSigner1 => is equal_to "stacks-signer-1".to_string(); "for StacksSigner1")] fn it_prints_correct_name_for_stateful_set(pod: StacksDevnetStatefulSet) -> String { pod.to_string() } @@ -39,6 +43,8 @@ fn it_prints_correct_name_for_stateful_set(pod: StacksDevnetStatefulSet) -> Stri #[test_case(StacksDevnetService::BitcoindNode => is equal_to "bitcoind-chain-coordinator".to_string(); "for BitcoindNode")] #[test_case(StacksDevnetService::StacksBlockchain => is equal_to "stacks-blockchain".to_string(); "for StacksBlockchain")] #[test_case(StacksDevnetService::StacksBlockchainApi => is equal_to "stacks-blockchain-api".to_string(); "for StacksBlockchainApi")] +#[test_case(StacksDevnetService::StacksSigner0 => is equal_to "stacks-signer-0".to_string(); "for StacksSigner0")] +#[test_case(StacksDevnetService::StacksSigner1 => is equal_to "stacks-signer-1".to_string(); "for StacksSigner1")] fn it_prints_correct_name_for_service(service: StacksDevnetService) -> String { service.to_string() } @@ -52,6 +58,8 @@ fn it_prints_correct_name_for_service(service: StacksDevnetService) -> String { #[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::API => is equal_to Some("3999".to_string()); "for StacksBlockchainApi API port")] #[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::Event => is equal_to Some("3700".to_string()); "for StacksBlockchainApi Event port")] #[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::DB => is equal_to Some("5432".to_string()); "for StacksBlockchainApi DB port")] +#[test_case(StacksDevnetService::StacksSigner0, ServicePort::Event => is equal_to Some("30001".to_string()); "for StacksSigner0 Event port")] +#[test_case(StacksDevnetService::StacksSigner1, ServicePort::Event => is equal_to Some("30001".to_string()); "for StacksSigner1 Event port")] #[test_case(StacksDevnetService::StacksBlockchainApi, ServicePort::RPC => is equal_to None; "invalid service port combination")] fn it_gets_correct_port_for_service( service: StacksDevnetService, @@ -63,14 +71,18 @@ fn it_gets_correct_port_for_service( #[test_case("bitcoin-node" => is equal_to Some(StacksDevnetService::BitcoindNode); "for bitcoin-node")] #[test_case("stacks-blockchain" => is equal_to Some(StacksDevnetService::StacksBlockchain); "for stacks-blockchain")] #[test_case("stacks-blockchain-api" => is equal_to Some(StacksDevnetService::StacksBlockchainApi); "for stacks-blockchain-api")] +#[test_case("stacks-signer-0" => is equal_to Some(StacksDevnetService::StacksSigner0); "for stacks-signer-0")] +#[test_case("stacks-signer-1" => is equal_to Some(StacksDevnetService::StacksSigner1); "for stacks-signer-1")] #[test_case("invalid" => is equal_to None; "returning None for invalid paths")] fn it_prints_service_from_path_part(path_part: &str) -> Option { get_service_from_path_part(path_part) } -#[test_case(StacksDevnetService::BitcoindNode => is equal_to Some("18443".to_string()); "for BitcoindNode")] -#[test_case(StacksDevnetService::StacksBlockchain => is equal_to Some("20443".to_string()); "for StacksBlockchain")] -#[test_case(StacksDevnetService::StacksBlockchainApi => is equal_to Some("3999".to_string()); "for StacksBlockchainApi")] +#[test_case(StacksDevnetService::BitcoindNode => is equal_to Some("18443".to_string()); "for BitcoindNode")] +#[test_case(StacksDevnetService::StacksBlockchain => is equal_to Some("20443".to_string()); "for StacksBlockchain")] +#[test_case(StacksDevnetService::StacksBlockchainApi => is equal_to Some("3999".to_string()); "for StacksBlockchainApi")] +#[test_case(StacksDevnetService::StacksSigner0 => is equal_to None; "for StacksSigner0")] +#[test_case(StacksDevnetService::StacksSigner1 => is equal_to None; "for StacksSigner1")] fn it_gets_user_facing_port_for_service(service: StacksDevnetService) -> Option { get_user_facing_port(service) } diff --git a/src/template_parser.rs b/src/template_parser.rs index afa97d0..d158444 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -49,5 +49,23 @@ pub fn get_yaml_from_resource(resource: StacksDevnetResource) -> &'static str { } StacksDevnetResource::Namespace => include_str!("../templates/namespace.template.yaml"), StacksDevnetResource::Pod(_) | StacksDevnetResource::Pvc(_) => unreachable!(), + StacksDevnetResource::StatefulSet(StacksDevnetStatefulSet::StacksSigner0) => { + include_str!("../templates/stateful-sets/stacks-signer-0.template.yaml") + } + StacksDevnetResource::Service(StacksDevnetService::StacksSigner0) => { + include_str!("../templates/services/stacks-signer-0.template.yaml") + } + StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksSigner0) => { + include_str!("../templates/configmaps/stacks-signer-0.template.yaml") + } + StacksDevnetResource::StatefulSet(StacksDevnetStatefulSet::StacksSigner1) => { + include_str!("../templates/stateful-sets/stacks-signer-1.template.yaml") + } + StacksDevnetResource::Service(StacksDevnetService::StacksSigner1) => { + include_str!("../templates/services/stacks-signer-1.template.yaml") + } + StacksDevnetResource::Configmap(StacksDevnetConfigmap::StacksSigner1) => { + include_str!("../templates/configmaps/stacks-signer-1.template.yaml") + } } } diff --git a/src/tests/fixtures/network-manifest.yaml b/src/tests/fixtures/network-manifest.yaml index e4d84b7..82d5ebe 100644 --- a/src/tests/fixtures/network-manifest.yaml +++ b/src/tests/fixtures/network-manifest.yaml @@ -107,6 +107,7 @@ devnet_settings: miner_mnemonic: fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce miner_derivation_path: "m/44'/5757'/0'/0/0" miner_coinbase_recipient: ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ + miner_wallet_name: "" faucet_stx_address: STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6 faucet_secret_key_hex: de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801 faucet_btc_address: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d @@ -124,19 +125,23 @@ devnet_settings: wallet: wallet_1 slots: 2 btc_address: mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC + auto_extend: ~ - start_at_cycle: 3 duration: 12 wallet: wallet_2 slots: 1 btc_address: muYdXKmX9bByAueDe6KFfHd5Ff1gdN9ErG + auto_extend: ~ - start_at_cycle: 3 duration: 12 wallet: wallet_3 slots: 1 btc_address: mvZtbibDAAA3WLpY7zXXFqRa3T4XSknBX7 + auto_extend: ~ execute_script: [] bitcoin_node_image_url: "quay.io/hirosystems/bitcoind:devnet-v3" stacks_node_image_url: "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0" + stacks_signer_image_url: "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0" stacks_api_image_url: "hirosystems/stacks-blockchain-api:latest" stacks_explorer_image_url: "hirosystems/explorer:latest" postgres_image_url: "postgres:14" @@ -172,6 +177,8 @@ devnet_settings: epoch_2_2: 103 epoch_2_3: 104 epoch_2_4: 105 - pox_2_activation: 102 + epoch_2_5: 105 + epoch_3_0: 121 use_docker_gateway_routing: false docker_platform: linux/amd64 + use_nakamoto: false diff --git a/src/tests/fixtures/project-manifest.yaml b/src/tests/fixtures/project-manifest.yaml index 2f08b5c..127246e 100644 --- a/src/tests/fixtures/project-manifest.yaml +++ b/src/tests/fixtures/project-manifest.yaml @@ -21,3 +21,5 @@ repl: trusted_sender: false trusted_caller: false callee_filter: false + clarity_wasm_mode: false + show_timings: false diff --git a/src/tests/fixtures/stacks-devnet-config.json b/src/tests/fixtures/stacks-devnet-config.json index fd57f0c..d5913f2 100644 --- a/src/tests/fixtures/stacks-devnet-config.json +++ b/src/tests/fixtures/stacks-devnet-config.json @@ -258,6 +258,7 @@ "miner_mnemonic": "fragile loan twenty basic net assault jazz absorb diet talk art shock innocent float punch travel gadget embrace caught blossom hockey surround initial reduce", "miner_derivation_path": "m/44'/5757'/0'/0/0", "miner_coinbase_recipient": "ST3Q96TFVE6E0Q91XVX6S8RWAJW5R8XTZ8YEBM8RQ", + "miner_wallet_name": "", "faucet_stx_address": "STNHKEPYEPJ8ET55ZZ0M5A34J0R3N5FM2CMMMAZ6", "faucet_secret_key_hex": "de433bdfa14ec43aa1098d5be594c8ffb20a31485ff9de2923b2689471c401b801", "faucet_btc_address": "mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d", @@ -295,6 +296,7 @@ "execute_script": [], "bitcoin_node_image_url": "quay.io/hirosystems/bitcoind:devnet-v3", "stacks_node_image_url": "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0", + "stacks_signer_image_url": "quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0", "stacks_api_image_url": "hirosystems/stacks-blockchain-api:latest", "stacks_explorer_image_url": "hirosystems/explorer:latest", "postgres_image_url": "postgres:14", @@ -330,9 +332,11 @@ "epoch_2_2": 103, "epoch_2_3": 104, "epoch_2_4": 105, - "pox_2_activation": 102, + "epoch_2_5": 105, + "epoch_3_0": 121, "use_docker_gateway_routing": false, - "docker_platform": "linux/amd64" + "docker_platform": "linux/amd64", + "use_nakamoto": false } }, "project_manifest": { @@ -360,7 +364,9 @@ "trusted_caller": false, "callee_filter": false } - } + }, + "clarity_wasm_mode": false, + "show_timings": false } } } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 86f3763..d37789a 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -31,7 +31,7 @@ const PRJ_NAME: &str = env!("CARGO_PKG_NAME"); fn get_version_info() -> String { format!("{{\"version\":\"{PRJ_NAME} v{VERSION}\"}}") } -fn get_template_config() -> StacksDevnetConfig { +fn get_template_config(use_nakamoto: bool) -> StacksDevnetConfig { let file_path = "src/tests/fixtures/stacks-devnet-config.json"; let file = File::open(file_path) .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", file_path, e)); @@ -41,12 +41,21 @@ fn get_template_config() -> StacksDevnetConfig { .read_to_end(&mut file_buffer) .unwrap_or_else(|e| panic!("unable to read file {}\n{:?}", file_path, e)); - let config_file: StacksDevnetConfig = match serde_json::from_slice(&file_buffer) { - Ok(s) => s, - Err(e) => { - panic!("Config file malformatted {}", e.to_string()); - } - }; + let config_file: StacksDevnetConfig = + match serde_json::from_slice::(&file_buffer) { + Ok(mut s) => { + if use_nakamoto { + if let Some(mut devnet) = s.network_manifest.devnet { + devnet.use_nakamoto = true; + s.network_manifest.devnet = Some(devnet); + } + }; + s + } + Err(e) => { + panic!("Config file malformatted {}", e.to_string()); + } + }; config_file } @@ -112,12 +121,13 @@ enum TestBody { CreateNetwork, } -#[test_case("/api/v1/network/{namespace}", Method::DELETE, None, false => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network DELETE request")] -#[test_case("/api/v1/network/{namespace}", Method::DELETE, None, true => using assert_cannot_delete_devnet_multiple_errs; "500 for network DELETE request with multiple errors")] -#[test_case("/api/v1/networks", Method::POST, Some(TestBody::CreateNetwork), true => using assert_cannot_create_devnet_err; "409 for create network POST request if devnet exists")] -#[test_case("/api/v1/network/{namespace}", Method::GET, None, true => using assert_get_network; "200 for network GET request to existing network")] -#[test_case("/api/v1/network/{namespace}", Method::HEAD, None, true => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network HEAD request to existing network")] -#[test_case("/api/v1/network/{namespace}/stacks-blockchain/v2/info/", Method::GET, None, true => using assert_failed_proxy; "proxies requests to downstream nodes")] +#[test_case("/api/v1/network/{namespace}", Method::DELETE, None, false, None => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network DELETE request")] +#[test_case("/api/v1/network/{namespace}", Method::DELETE, None, true, None => using assert_cannot_delete_devnet_multiple_errs; "500 for network DELETE request with multiple errors")] +#[test_case("/api/v1/networks", Method::POST, Some(TestBody::CreateNetwork), true, None => using assert_cannot_create_devnet_err; "409 for create network POST request if devnet exists")] +#[test_case("/api/v1/network/{namespace}", Method::GET, None, true, None => using assert_get_network; "200 for network GET request to existing network")] +#[test_case("/api/v1/network/{namespace}", Method::HEAD, None, true, None => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network HEAD request to existing network")] +#[test_case("/api/v1/network/{namespace}", Method::HEAD, None, true, Some(true) => is equal_to (StatusCode::OK, "Ok".to_string()); "200 for network HEAD request to existing network; use_nakamoto")] +#[test_case("/api/v1/network/{namespace}/stacks-blockchain/v2/info/", Method::GET, None, true, None => using assert_failed_proxy; "proxies requests to downstream nodes")] #[serial_test::serial] #[tokio::test] #[cfg_attr(not(feature = "k8s_tests"), ignore)] @@ -126,6 +136,7 @@ async fn it_responds_to_valid_requests_with_deploy( method: Method, body: Option, tear_down: bool, + use_nakamoto: Option, ) -> (StatusCode, String) { let namespace = &get_random_namespace(); @@ -141,7 +152,7 @@ async fn it_responds_to_valid_requests_with_deploy( let _ = k8s_manager.deploy_namespace(&namespace).await.unwrap(); - let mut config = get_template_config(); + let mut config = get_template_config(use_nakamoto.unwrap_or(false)); config.namespace = namespace.to_owned(); let validated_config = config.to_validated_config(&namespace, ctx.clone()).unwrap(); let user_id = &namespace; @@ -152,7 +163,7 @@ async fn it_responds_to_valid_requests_with_deploy( let body = match body { None => Body::empty(), Some(TestBody::CreateNetwork) => { - let mut config = get_template_config(); + let mut config = get_template_config(use_nakamoto.unwrap_or(false)); config.namespace = namespace.to_owned(); Body::from(serde_json::to_string(&config).unwrap()) } diff --git a/templates/configmaps/stacks-signer-0.template.yaml b/templates/configmaps/stacks-signer-0.template.yaml new file mode 100644 index 0000000..c47cd8c --- /dev/null +++ b/templates/configmaps/stacks-signer-0.template.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +data: + Signer.toml: "{data}" +kind: ConfigMap +metadata: + name: stacks-signer-0 + namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 + app.kubernetes.io/component: stacks-signer-0 + argocd.argoproj.io/instance: platform-user-resources.platform \ No newline at end of file diff --git a/templates/configmaps/stacks-signer-1.template.yaml b/templates/configmaps/stacks-signer-1.template.yaml new file mode 100644 index 0000000..96b76a7 --- /dev/null +++ b/templates/configmaps/stacks-signer-1.template.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +data: + Signer.toml: "{data}" +kind: ConfigMap +metadata: + name: stacks-signer-1 + namespace: "{namespace}" + labels: + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 + app.kubernetes.io/component: stacks-signer-1 + argocd.argoproj.io/instance: platform-user-resources.platform \ No newline at end of file diff --git a/templates/deployments/bitcoind-chain-coordinator.template.yaml b/templates/deployments/bitcoind-chain-coordinator.template.yaml index 2f1ac1a..9dcd577 100644 --- a/templates/deployments/bitcoind-chain-coordinator.template.yaml +++ b/templates/deployments/bitcoind-chain-coordinator.template.yaml @@ -38,7 +38,7 @@ spec: - -conf=/etc/bitcoin/bitcoin.conf - -nodebuglogfile - -pid=/run/bitcoind.pid - image: quay.io/hirosystems/bitcoind:devnet-v3 + image: quay.io/hirosystems/bitcoind:26.0 imagePullPolicy: IfNotPresent name: bitcoind ports: @@ -71,7 +71,7 @@ spec: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - image: hirosystems/stacks-network-orchestrator@sha256:e9c88e46adb10deba74e29883533de747a2200665f919d1708a5ea0f2638d32a + image: hirosystems/stacks-network-orchestrator:clarinet-2.4.0-beta2 imagePullPolicy: IfNotPresent name: chain-coordinator ports: diff --git a/templates/deployments/stacks-blockchain.template.yaml b/templates/deployments/stacks-blockchain.template.yaml index 824c1a2..ff8d70a 100644 --- a/templates/deployments/stacks-blockchain.template.yaml +++ b/templates/deployments/stacks-blockchain.template.yaml @@ -36,15 +36,14 @@ spec: - command: - stacks-node - start - - --config=/src/stacks-blockchain/Stacks.toml + - --config + - /src/stacks-blockchain/Stacks.toml env: - name: STACKS_LOG_PP value: "1" - name: BLOCKSTACK_USE_TEST_GENESIS_CHAINSTATE value: "1" - - name: STACKS_LOG_DEBUG - value: "0" - image: quay.io/hirosystems/stacks-node:devnet-v3 + image: quay.io/hirosystems/stacks-node:devnet-2.4.0.0.0 imagePullPolicy: IfNotPresent name: stacks-blockchain ports: diff --git a/templates/services/stacks-signer-0.template.yaml b/templates/services/stacks-signer-0.template.yaml new file mode 100644 index 0000000..bcda393 --- /dev/null +++ b/templates/services/stacks-signer-0.template.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: stacks-signer-0 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 + argocd.argoproj.io/instance: platform-user-resources.platform + name: stacks-signer-0 + namespace: "{namespace}" +spec: + ports: + - name: http-signer-event + port: 30001 + protocol: TCP + targetPort: 30001 + selector: + app.kubernetes.io/component: stacks-signer-0 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 diff --git a/templates/services/stacks-signer-1.template.yaml b/templates/services/stacks-signer-1.template.yaml new file mode 100644 index 0000000..5cdf09b --- /dev/null +++ b/templates/services/stacks-signer-1.template.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: stacks-signer-1 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 + argocd.argoproj.io/instance: platform-user-resources.platform + name: stacks-signer-1 + namespace: "{namespace}" +spec: + ports: + - name: http-signer-event + port: 30001 + protocol: TCP + targetPort: 30001 + selector: + app.kubernetes.io/component: stacks-signer-1 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 diff --git a/templates/stateful-sets/stacks-signer-0.template.yaml b/templates/stateful-sets/stacks-signer-0.template.yaml new file mode 100644 index 0000000..2df4988 --- /dev/null +++ b/templates/stateful-sets/stacks-signer-0.template.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: stacks-signer-0 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 + argocd.argoproj.io/instance: platform-user-resources.platform + name: stacks-signer-0 + namespace: "{namespace}" +spec: + replicas: 1 + serviceName: stacks-signer-0 + selector: + matchLabels: + app.kubernetes.io/component: stacks-signer-0 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 + template: + metadata: + labels: + app.kubernetes.io/component: stacks-signer-0 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-0 + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - name: stacks-signer-0 + command: + - stacks-signer + - run + - --config=/src/stacks-signer-0/Signer.toml + image: quay.io/hirosystems/stacks-node:devnet-with-signer-beta4 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 30001 + name: signer-event + protocol: TCP + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumeMounts: + - mountPath: /chainstate/ + name: signer-db + - mountPath: /src/stacks-signer-0 + name: stacks-signer-0 + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumes: + - configMap: + name: stacks-signer-0 + name: stacks-signer-0 + volumeClaimTemplates: + - metadata: + name: signer-db + spec: + accessModes: + - ReadWriteOnce + storageClassName: premium-rwo + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/templates/stateful-sets/stacks-signer-1.template.yaml b/templates/stateful-sets/stacks-signer-1.template.yaml new file mode 100644 index 0000000..511d5ad --- /dev/null +++ b/templates/stateful-sets/stacks-signer-1.template.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: stacks-signer-1 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 + argocd.argoproj.io/instance: platform-user-resources.platform + name: stacks-signer-1 + namespace: "{namespace}" +spec: + replicas: 1 + serviceName: stacks-signer-1 + selector: + matchLabels: + app.kubernetes.io/component: stacks-signer-1 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 + template: + metadata: + labels: + app.kubernetes.io/component: stacks-signer-1 + app.kubernetes.io/instance: "{user_id}" + app.kubernetes.io/managed-by: stacks-devnet-api + app.kubernetes.io/name: stacks-signer-1 + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-preemptible + operator: DoesNotExist + containers: + - name: stacks-signer-1 + command: + - stacks-signer + - run + - --config=/src/stacks-signer-1/Signer.toml + image: quay.io/hirosystems/stacks-node:devnet-with-signer-beta4 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 30001 + name: signer-event + protocol: TCP + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumeMounts: + - mountPath: /chainstate/ + name: signer-db + - mountPath: /src/stacks-signer-1 + name: stacks-signer-1 + readOnly: true + resources: + requests: + cpu: 250m + memory: 750Mi # todo: revisit allocation + limits: + memory: 750Mi # todo: revisit allocation + volumes: + - configMap: + name: stacks-signer-1 + name: stacks-signer-1 + volumeClaimTemplates: + - metadata: + name: signer-db + spec: + accessModes: + - ReadWriteOnce + storageClassName: premium-rwo + resources: + requests: + storage: 1Gi \ No newline at end of file From 36eb754ec71372af67036283ec50bcd98e5b6ca3 Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Mon, 25 Mar 2024 15:19:04 -0600 Subject: [PATCH 34/35] chore: bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4116def..ec5b431 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3704,7 +3704,7 @@ dependencies = [ [[package]] name = "stacks-devnet-api" -version = "0.1.0" +version = "0.2.0" dependencies = [ "chainhook-types", "clarinet-deployments", diff --git a/Cargo.toml b/Cargo.toml index 79f7dc3..9295044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stacks-devnet-api" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Micaiah Reid "] description = "The Stacks Devnet API runs a server that can be used to deploy, delete, manage, and make requests to Stacks Devnets run on Kubernetes." From a566ed9ece2e43d28d66d4c2340c6d9fb3ee10b9 Mon Sep 17 00:00:00 2001 From: MicaiahReid Date: Mon, 25 Mar 2024 15:31:05 -0600 Subject: [PATCH 35/35] fix version number --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec5b431..70c8e73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3704,7 +3704,7 @@ dependencies = [ [[package]] name = "stacks-devnet-api" -version = "0.2.0" +version = "1.1.0" dependencies = [ "chainhook-types", "clarinet-deployments", diff --git a/Cargo.toml b/Cargo.toml index 9295044..c80e435 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stacks-devnet-api" -version = "0.2.0" +version = "1.1.0" edition = "2021" authors = ["Micaiah Reid "] description = "The Stacks Devnet API runs a server that can be used to deploy, delete, manage, and make requests to Stacks Devnets run on Kubernetes."