diff --git a/Cargo.lock b/Cargo.lock index b4ee58cef0..891407065e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2462,6 +2462,19 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +[[package]] +name = "e2e" +version = "1.7.0" +dependencies = [ + "bech32 0.11.0", + "hex", + "log", + "rand 0.9.1", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -12585,6 +12598,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "valuable" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 2a7304470a..daf4e7a957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ members = [ "toolkit/utils/db-sync-sqlx", "toolkit/governed-map/primitives", "toolkit/governed-map/pallet", + "e2e", ] resolver = "2" @@ -184,6 +185,7 @@ sqlx = { version = "0.8.5", default-features = false, features = [ ] } derive-where = { version = "1.2.7", default-features = false } once_cell = { version = "1.21.3", default-features = false } +uuid = { version = "1.16.0", default-features = false } # substrate dependencies frame-benchmarking = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2503-5" } diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml new file mode 100644 index 0000000000..6f9df0a630 --- /dev/null +++ b/e2e/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "e2e" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +bech32.workspace = true +bech32.features = ["alloc"] +hex.workspace = true +log.workspace = true +rand.features = ["thread_rng"] +rand.workspace = true +serde.workspace = true +serde_json.workspace = true +uuid.features = ["v4"] +uuid.workspace = true + +[lints] +workspace = true diff --git a/e2e/config/config.json b/e2e/config/config.json new file mode 100644 index 0000000000..14c77d5e7c --- /dev/null +++ b/e2e/config/config.json @@ -0,0 +1,34 @@ +{ + "atms_kind": "plain-ecdsa-secp256k1", + "committee_epoch_slippage": 0, + "committee_participation_tolerance": 0.05, + "max_validators": 32, + "main_chain": { + "network": "--testnet-magic 2", + "epoch_length": 86400, + "slot_length": 1, + "active_slots_coeff": 0.05, + "security_param": 432, + "init_timestamp": 1666656000, + "block_stability_margin": 0 + }, + "nodes_config": { + "default_scheme": "ws", + "default_rpc_scheme": "http", + "default_port": 9933, + "reserve": { + "token_name": "Reward token", + "v_function_script_path": "./config/scripts/v-function-1975.script", + "v_function_updated_script_path": "./config/scripts/v-function-2025.script" + } + }, + "timeouts": { + "long_running_function": 50, + "register_cmd": 300, + "deregister_cmd": 300, + "main_chain_tx": 180 + }, + "poll_intervals": { + "transaction_finalization": 1 + } +} diff --git a/e2e/config/examples/multisig_governance.yaml b/e2e/config/examples/multisig_governance.yaml new file mode 100644 index 0000000000..42059bc5d2 --- /dev/null +++ b/e2e/config/examples/multisig_governance.yaml @@ -0,0 +1,7 @@ +# Sample configuration for multisig governance testing +nodes_config: + # Existing single governance authority configuration + governance_authority: + mainchain_address: "addr_test1vryefctfmmruwnqh287q053narzqn5duh53kk2hxw8jjswsuqfveh" + mainchain_key: "secrets/substrate/devnet/keys/governance_authority/payment.skey" + mainchain_pub_key_hash: "c994e169dec7c74c1751fc07d233e8c409d1bcbd236b2ae671e5283a" \ No newline at end of file diff --git a/e2e/config/scripts/v-function-1975.script b/e2e/config/scripts/v-function-1975.script new file mode 100644 index 0000000000..9c0eb3e6e5 --- /dev/null +++ b/e2e/config/scripts/v-function-1975.script @@ -0,0 +1,5 @@ +{ + "type": "PlutusScriptV2", + "description": "V-function that allows to release one token each minute since 1975-07-02 21:36 UTC (timestamp 000173568960)", + "cborHex": "59072659072301000033233223222253232335332232353232325333573466e1d20000021323232323232332212330010030023232325333573466e1d2000002132323232323232323232332323233323333323332332332222222222221233333333333300100d00c00b00a00900800700600500400300230013574202460026ae84044c00c8c8c8c94ccd5cd19b87480000084cc8848cc00400c008c070d5d080098029aba135744002260489201035054310035573c0046aae74004dd5000998018009aba100f23232325333573466e1d20000021323232333322221233330010050040030023232325333573466e1d20000021332212330010030023020357420026600803e6ae84d5d100089814a481035054310035573c0046aae74004dd51aba1004300835742006646464a666ae68cdc3a4000004224440062a666ae68cdc3a4004004264244460020086eb8d5d08008a999ab9a3370e9002001099091118010021aba100113029491035054310035573c0046aae74004dd51aba10023300175c6ae84d5d1001111919192999ab9a3370e900100108910008a999ab9a3370e9000001099091180100198029aba10011302a491035054310035573c0046aae74004dd50009aba20013574400226046921035054310035573c0046aae74004dd500098009aba100d30013574201860046004eb4cc00404cd5d080519980200a3ad35742012646464a666ae68cdc3a40000042646466442466002006004646464a666ae68cdc3a40000042664424660020060046600aeb4d5d080098021aba1357440022604c921035054310035573c0046aae74004dd51aba10033232325333573466e1d20000021332212330010030023300575a6ae84004c010d5d09aba2001130264901035054310035573c0046aae74004dd51aba1357440064646464a666ae68cdc3a400000420482a666ae68cdc3a4004004204a2604c921035054310035573c0046aae74004dd5000911919192999ab9a3370e9000001089110010a999ab9a3370e90010010990911180180218029aba100115333573466e1d20040021122200113026491035054310035573c0046aae74004dd500089810a49035054310035573c0046aae74004dd51aba10083300175c6ae8401c8c88c008dd60009813111999aab9f0012028233502730043574200460066ae88008084ccc00c044008d5d0802998008011aba1004300275c40024464460046eac004c09088cccd55cf800901311919a8131991091980080180118031aab9d001300535573c00260086ae8800cd5d080100f98099aba1357440026ae88004d5d10009aba2001357440026ae88004d5d10009aba2001357440026ae88004d5d100089808249035054310035573c0046aae74004dd51aba10073001357426ae8801c8c8c8c94ccd5cd19b87480000084c848888c00c014dd71aba100115333573466e1d20020021321222230010053008357420022a666ae68cdc3a400800426424444600400a600c6ae8400454ccd5cd19b87480180084c848888c010014c014d5d080089808249035054310035573c0046aae74004dd500091919192999ab9a3370e900000109909111111180280418029aba100115333573466e1d20020021321222222230070083005357420022a666ae68cdc3a400800426644244444446600c012010600a6ae84004dd71aba1357440022a666ae68cdc3a400c0042664424444444660040120106eb8d5d08009bae357426ae8800454ccd5cd19b87480200084cc8848888888cc004024020dd71aba1001375a6ae84d5d10008a999ab9a3370e90050010891111110020a999ab9a3370e900600108911111100189807a49035054310035573c0046aae74004dd500091919192999ab9a3370e9000001099091180100198029aba100115333573466e1d2002002132333222122333001005004003375a6ae84008dd69aba1001375a6ae84d5d10009aba20011300e4901035054310035573c0046aae74004dd500091919192999ab9a3370e900000109909118010019bae357420022a666ae68cdc3a400400426424460020066eb8d5d080089806a481035054310035573c0046aae74004dd500091919192999ab9a3370e900000109991091980080180118029aba1001375a6ae84d5d1000898062481035054310035573c0046aae74004dd500091919192999ab9a3370e900000109bae3574200226016921035054310035573c0046aae74004dd500089803a49035054310035573c0046aae74004dd5003111999a8009002919199ab9a337126602044a66a002290001109a801112999ab9a3371e004010260260022600c006600244444444444401066e0ccdc09a9a980091111111111100291001112999a80110a99a80108008b0b0b002a4181520e00e00ca006400a400a6eb401c48800848800440084c00524010350543500232633573800200424002600644a66a002290001109a8011119b800013006003122002122122330010040032323001001223300330020020014c01051a677485800001" +} diff --git a/e2e/config/scripts/v-function-2025.script b/e2e/config/scripts/v-function-2025.script new file mode 100644 index 0000000000..0c58942b91 --- /dev/null +++ b/e2e/config/scripts/v-function-2025.script @@ -0,0 +1,5 @@ +{ + "type": "PlutusScriptV2", + "description": "V-function that allows to release one token each minute since 2025-02-07 10:46 UTC (timestamp 1738925213000)", + "cborHex": "59072a59072701000033233223222253232335332232353232325333573466e1d20000021323232323232332212330010030023232325333573466e1d2000002132323232323232323232332323233323333323332332332222222222221233333333333300100d00c00b00a00900800700600500400300230013574202460026ae84044c00c8c8c8c94ccd5cd19b87480000084cc8848cc00400c008c070d5d080098029aba135744002260489201035054310035573c0046aae74004dd5000998018009aba100f23232325333573466e1d20000021323232333322221233330010050040030023232325333573466e1d20000021332212330010030023020357420026600803e6ae84d5d100089814a481035054310035573c0046aae74004dd51aba1004300835742006646464a666ae68cdc3a4000004224440062a666ae68cdc3a4004004264244460020086eb8d5d08008a999ab9a3370e9002001099091118010021aba100113029491035054310035573c0046aae74004dd51aba10023300175c6ae84d5d1001111919192999ab9a3370e900100108910008a999ab9a3370e9000001099091180100198029aba10011302a491035054310035573c0046aae74004dd50009aba20013574400226046921035054310035573c0046aae74004dd500098009aba100d30013574201860046004eb4cc00404cd5d080519980200a3ad35742012646464a666ae68cdc3a40000042646466442466002006004646464a666ae68cdc3a40000042664424660020060046600aeb4d5d080098021aba1357440022604c921035054310035573c0046aae74004dd51aba10033232325333573466e1d20000021332212330010030023300575a6ae84004c010d5d09aba2001130264901035054310035573c0046aae74004dd51aba1357440064646464a666ae68cdc3a400000420482a666ae68cdc3a4004004204a2604c921035054310035573c0046aae74004dd5000911919192999ab9a3370e9000001089110010a999ab9a3370e90010010990911180180218029aba100115333573466e1d20040021122200113026491035054310035573c0046aae74004dd500089810a49035054310035573c0046aae74004dd51aba10083300175c6ae8401c8c88c008dd60009813111999aab9f0012028233502730043574200460066ae88008084ccc00c044008d5d0802998008011aba1004300275c40024464460046eac004c09088cccd55cf800901311919a8131991091980080180118031aab9d001300535573c00260086ae8800cd5d080100f98099aba1357440026ae88004d5d10009aba2001357440026ae88004d5d10009aba2001357440026ae88004d5d100089808249035054310035573c0046aae74004dd51aba10073001357426ae8801c8c8c8c94ccd5cd19b87480000084c848888c00c014dd71aba100115333573466e1d20020021321222230010053008357420022a666ae68cdc3a400800426424444600400a600c6ae8400454ccd5cd19b87480180084c848888c010014c014d5d080089808249035054310035573c0046aae74004dd500091919192999ab9a3370e900000109909111111180280418029aba100115333573466e1d20020021321222222230070083005357420022a666ae68cdc3a400800426644244444446600c012010600a6ae84004dd71aba1357440022a666ae68cdc3a400c0042664424444444660040120106eb8d5d08009bae357426ae8800454ccd5cd19b87480200084cc8848888888cc004024020dd71aba1001375a6ae84d5d10008a999ab9a3370e90050010891111110020a999ab9a3370e900600108911111100189807a49035054310035573c0046aae74004dd500091919192999ab9a3370e9000001099091180100198029aba100115333573466e1d2002002132333222122333001005004003375a6ae84008dd69aba1001375a6ae84d5d10009aba20011300e4901035054310035573c0046aae74004dd500091919192999ab9a3370e900000109909118010019bae357420022a666ae68cdc3a400400426424460020066eb8d5d080089806a481035054310035573c0046aae74004dd500091919192999ab9a3370e900000109991091980080180118029aba1001375a6ae84d5d1000898062481035054310035573c0046aae74004dd500091919192999ab9a3370e900000109bae3574200226016921035054310035573c0046aae74004dd500089803a49035054310035573c0046aae74004dd5003111999a8009002919199ab9a337126602044a66a002290001109a801112999ab9a3371e004010260260022600c006600244444444444401066e0ccdc09a9a980091111111111100291001112999a80110a99a80108008b0b0b002a4181520e00e00ca006400a400a6eb401c48800848800440084c00524010350543500232633573800200424002600644a66a002290001109a8011119b800013006003122002122122330010040032323001001223300330020020014c01091b00000194e00505480001" +} diff --git a/e2e/config/substrate/ci-ci.json b/e2e/config/substrate/ci-ci.json new file mode 100644 index 0000000000..491aee8cb9 --- /dev/null +++ b/e2e/config/substrate/ci-ci.json @@ -0,0 +1,29 @@ +{ + "nodes_config": { + "nodes": { + "validator-1": { + "host": "ci-preview-validator-1-service.ci-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-2": { + "host": "ci-preview-validator-2-service.ci-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-3": { + "host": "ci-preview-validator-3-service.ci-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-4": { + "host": "ci-preview-validator-4-service.ci-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-5": { + "host": "ci-preview-validator-5-service.ci-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + } + } + }, + "stack_config": { + "tools_host": "binary-host-service.sc.svc.cluster.local" + } +} \ No newline at end of file diff --git a/e2e/config/substrate/ci_nodes.json b/e2e/config/substrate/ci_nodes.json new file mode 100644 index 0000000000..493812630e --- /dev/null +++ b/e2e/config/substrate/ci_nodes.json @@ -0,0 +1,118 @@ +{ + "deployment_mc_epoch": 925, + "genesis_utxo": "d334afbb10b16d9b8bc1537d1da0d7c1b6b46ac71666a3a38161a39a0223db01#1", + "deployment_version": "master", + "test_environment": "ci", + "keys_path": "secrets/substrate/staging/keys", + "nodes_config": { + "nodes": { + "validator-1": { + "host": "10.0.10.139", + "port": "30621", + "aura_ss58_address": "5GHLr2zBDNPXno9XdKgh541uRSiTxnZyzFcr4jK6HUbHMpit", + "pool_id": "da74fc8256d15c7ab3370a6ca28398986cb97c32e9ef66026ac61e99", + "public_key": "0x03b827f4da9711bab7292e5695576a841a4d20af9a07b1ba7a230168d2a78e9df4", + "aura_public_key": "0xba94651de6279a38a416b97b9720c3df76224435e951ac73e9e302a4ee9fcf73", + "grandpa_public_key": "0xdde2501588713ddad5daf5a898c19d82cd591609c9184679868640c8cfe8287d", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vq6ywn0f007x32j47jrk5qy9hy3gknsvszrcpdqkeaye7pshu2w2t", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-1/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-1/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-1/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-1/partner_chain.skey.json.decrypted" + } + }, + "validator-2": { + "host": "10.0.10.139", + "port": "30622", + "aura_ss58_address": "5DHbxU687f1Y3x8yBCWMtqSiJ5qt2yrxQPXNXZNNDaFtmXKv", + "pool_id": "eaed153a8b046770cfd094ee72d080ea682188e63ac11937e3f7f827", + "public_key": "0x02ef5bcd94d54a18ad199559782cd72ac3ccd850976aaaafbca8f9d2625afbf7c4", + "aura_public_key": "0x36128fff2acc04f206ccaf4e9f8e9995998efced29075a58b7d76d3735c21208", + "grandpa_public_key": "0x8f9a9856a27cc114ce85b64f41144f0c907c4bd8b3102b083b52b6b61aff6c47", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vz80jkz5d2kdaykfrqlqryr6lt46tunk4ldfjqv88hevrpsrdfmm5", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-2/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-2/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-2/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-2/partner_chain.skey.json.decrypted" + } + }, + "validator-3": { + "host": "10.0.10.139", + "port": "30623", + "aura_ss58_address": "5FYtL6HccYhk6KZeFP7hNnkMaXrAwVpTHJWsfnNJcu8AM6in", + "pool_id": "7dfba85597a867fffa59037df7f6adcd50e745dcceac2b48eda94b20", + "public_key": "0x02f2762ab6e1a125dc03908a7b738f8023d13763f28a11d7633c6c8bc463478430", + "aura_public_key": "0x9a32d3896a56e822321f7bc915befc8ce112c5d67e3c6497295bd3d7b020f94c", + "grandpa_public_key": "0x4f3c0ecc6dc474f27ad7967f5cdbd50da047ffedbc91b65f5cd247515489c98f", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vrxc6wvtmqrqx4n9e89sqf9u0w0jcp8a96s7gkddfsr0ppcdvzcf6", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-3/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-3/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-3/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-3/partner_chain.skey.json.decrypted" + } + }, + "validator-4": { + "host": "10.0.10.139", + "port": "30624", + "aura_ss58_address": "5GVpqdtqjxqUjuVKMkmh8ehSwcs2nXjpvzHqjouZXMJAyC4b", + "pool_id": "2a3f5dd02da1310e081f2367412e02b72baad3e2a5045f62df2c78c5", + "public_key": "0x025e19f82c5e2bac5e8869d49ff26359e442628bc5cfa38eeb5275f43d04015da8", + "aura_public_key": "0xc41992b8eb2f3a8a6c46211df584827f9eeb0175e2c75e1242392262b55b6874", + "grandpa_public_key": "0x34b71fdad96431bf115350d8ad21eec07a2b154ff32dc31125f988e308bebea8", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vq65mgmcpd6rq6ndy22kwxzdc9u0tmrpr7s30037qdhlalg3f5ax0", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-4/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-4/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-4/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-4/partner_chain.skey.json.decrypted" + } + }, + "validator-5": { + "host": "10.0.10.139", + "port": "30625", + "aura_ss58_address": "5DsfhT7HJe6i5LYeKBzefrXijW5UgPsn2Cuyw5WMa4uEktTn", + "pool_id": "ae81beee7a6c3fa13bba811f91f63ebdd7eb25dd8a62476d4996de10", + "public_key": "0x03f38a062a4b372c045c1dddc4fe98a2c9cb1d6eec8bf02f973fd29b1096cd8155", + "aura_public_key": "0x500d7ff6d903c85db5ee5624df9510c2a085cf30da260166bd370010d0bdc97a", + "grandpa_public_key": "0xa04d74c1539550876d04e4d2de4e0531087c3b6810ce96ddc16d78ccf4ac4f11", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vqqm36dcg5xeeqgc29r8gu6wujlmr4fyrdrvuzq4qnem2vs7g2kvj", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-5/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-5/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-5/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-5/partner_chain.skey.json.decrypted" + } + } + }, + "governance_authority": { + "mainchain_address": "addr_test1vq0sjaaupatuvl9x6aefdsd4whlqtfku93068qzkhf3u2rqt9cnuq", + "mainchain_key": "./secrets/substrate/staging/keys/governance_authority/init.skey.json.decrypted", + "mainchain_pub_key": "a90ba46b07c0c2c940e3e3149aee5817130a1d8b600bc85b91345e4f7a1e5aca" + }, + "selected_node": "validator-4", + "node": "${nodes_config[nodes][${nodes_config[selected_node]}]}", + "token_conversion_rate": 9, + "block_duration": 6, + "slots_in_epoch": 60, + "token_policy_id": "", + "d_param_min": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 + }, + "d_param_max": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 + }, + "reserve": { + "token_name": "PCTokenDemo" + } + } +} diff --git a/e2e/config/substrate/ci_stack.json b/e2e/config/substrate/ci_stack.json new file mode 100644 index 0000000000..c618e99fc1 --- /dev/null +++ b/e2e/config/substrate/ci_stack.json @@ -0,0 +1,29 @@ +{ + "stack_config": { + "ogmios_scheme": "ws", + "ogmios_host": "devnet-services-service", + "ogmios_port": 1337, + "tools_host": "10.0.12.163", + "ssh": { + "username": "root", + "host": "${stack_config[tools_host]}", + "port": 22, + "host_keys_path": "config/substrate/known_hosts", + "private_key_path": "secrets/substrate/staging/keys/ssh-key.yaml.decrypted" + }, + "tools": { + "cardano_cli": { + "cli": "export CARDANO_NODE_SOCKET_PATH=/ipc/node.socket && /tools/cardano-cli", + "ssh": "${stack_config[ssh]}" + }, + "partner_chains_node": { + "cli": "/tools/ci-preview/partner-chains-node-latest", + "ssh": "${stack_config[ssh]}" + }, + "bech32": { + "cli": "/tools/bech32", + "ssh": "${stack_config[ssh]}" + } + } + } +} diff --git a/e2e/config/substrate/devnet-ci.json b/e2e/config/substrate/devnet-ci.json new file mode 100644 index 0000000000..4c54b29917 --- /dev/null +++ b/e2e/config/substrate/devnet-ci.json @@ -0,0 +1,41 @@ +{ + "nodes_config": { + "nodes": { + "alice": { + "host": "alice-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "bob": { + "host": "bob-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "charlie": { + "host": "charlie-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "dave": { + "host": "dave-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "eve": { + "host": "eve-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "ferdie": { + "host": "ferdie-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "greg": { + "host": "greg-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "henry": { + "host": "henry-service.sc.svc.cluster.local", + "port": "${nodes_config[default_port]}" + } + } + }, + "stack_config": { + "tools_host": "binary-host-service.sc.svc.cluster.local" + } +} \ No newline at end of file diff --git a/e2e/config/substrate/devnet_nodes.json b/e2e/config/substrate/devnet_nodes.json new file mode 100644 index 0000000000..4e3d68f087 --- /dev/null +++ b/e2e/config/substrate/devnet_nodes.json @@ -0,0 +1,114 @@ +{ + "deployment_mc_epoch": 941, + "genesis_utxo": "f9321b197b0995c5f40e30425b1f3c5a797c4cb2678e1ed44934bc50858a9278#1", + "deployment_version": "v1.7.0-rc1", + "test_environment": "devnet", + "committee_participation_tolerance": 0.21, + "nodes_config": { + "nodes": { + "alice": { + "host": "10.0.10.13", + "port": "30021", + "aura_ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "pool_id": "17a3e09033f190cdcddd11350f095d3ae11f1a61c65731366750177a", + "public_key": "0x020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1", + "aura_public_key": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "grandpa_public_key": "0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee", + "permissioned_candidate": true + }, + "bob": { + "host": "10.0.10.13", + "port": "30022", + "aura_ss58_address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "pool_id": "dd67a558342e53c9ef9528124e44e43a34b2ae3b432aa5144ffec24d", + "public_key": "0x0390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27", + "aura_public_key": "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "grandpa_public_key": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69", + "permissioned_candidate": true + }, + "charlie": { + "host": "10.0.10.13", + "port": "30023", + "aura_ss58_address": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + "pool_id": "f824ff53b6a71522bb9f8db18fffd2469d957a56cf00e0cb0bdc8a08", + "public_key": "0x0389411795514af1627765eceffcbd002719f031604fadd7d188e2dc585b4e1afb", + "aura_public_key": "0x90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22", + "grandpa_public_key": "0x439660b36c6c03afafca027b910b4fecf99801834c62a5e6006f27d978de234f", + "permissioned_candidate": true + }, + "dave": { + "host": "10.0.10.13", + "port": "30024", + "aura_ss58_address": "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", + "pool_id": "2e36e13ad5665685f163e4fa3d32ccf0a333fda0a67c93cbac99f7a2", + "public_key": "0x03bc9d0ca094bd5b8b3225d7651eac5d18c1c04bf8ae8f8b263eebca4e1410ed0c", + "aura_public_key": "0x306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20", + "grandpa_public_key": "0x5e639b43e0052c47447dac87d6fd2b6ec50bdd4d0f614e4299c665249bbd09d9" + }, + "eve": { + "host": "10.0.10.13", + "port": "30025", + "aura_ss58_address": "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", + "pool_id": "54512ccbdf3a6912bf4da983a4aa46e9c46eb0fd19a2b3f5341c5366", + "public_key": "0x031d10105e323c4afce225208f71a6441ee327a65b9e646e772500c74d31f669aa", + "aura_public_key": "0xe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e", + "grandpa_public_key": "0x1dfe3e22cc0d45c70779c1095f7489a8ef3cf52d62fbd8c2fa38c9f1723502b5", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vr88zd5ywcdu07hqpnutd8umqyld34e6rrtzgu7m3rua67gg6clcz", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/devnet/keys/eve/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/devnet/keys/eve/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/devnet/keys/eve/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/devnet/keys/eve/partner_chain.skey.json.decrypted" + } + }, + "ferdie": { + "host": "10.0.10.13", + "port": "30026", + "aura_ss58_address": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "pool_id": "9ad70551d17317e44866850f9827f6464f0867e50bff5a87b1f9e3c0", + "public_key": "0x0291f1217d5a04cb83312ee3d88a6e6b33284e053e6ccfc3a90339a0299d12967c", + "aura_public_key": "0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "grandpa_public_key": "0x568cb4a574c6d178feb39c27dfc8b3f789e5f5423e19c71633c748b9acf086b5", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vrkp4j33xcysqfancc7fttdrwf86ztgpaqhhzu98vahnevqfng7r9", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/devnet/keys/ferdie/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/devnet/keys/ferdie/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/devnet/keys/ferdie/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/devnet/keys/ferdie/partner_chain.skey.json.decrypted" + } + }, + "greg": { + "host": "10.0.10.13", + "port": "30027", + "aura_ss58_address": "5D4oNr3wasgzxt7KdoHBjnxUthfoM93kWMav63aoYmPHfkBW", + "pool_id": "", + "public_key": "0x02dacce90fca29ca80404d9b4e8ff3d9dabd03def6a82e412acb2ad04dd734dbfc", + "aura_public_key": "0x2c4ed1038f6e4131c21b6b89885ed232c5b81bae09009376e9079cc8aa518a1c", + "grandpa_public_key": "0xfa41bacb202b0529288b05af1b324f85fe561091c2d29d9df1df37c3aa687c23", + "permissioned_candidate": false + }, + "henry": { + "host": "10.0.10.13", + "port": "30028", + "aura_ss58_address": "5FcTxwLAQ8L23HvTa6Y6UUMBKJkYRG42Vg9wVVpZDpE2ZnTZ", + "pool_id": "", + "public_key": "0x0263c9cdabbef76829fe5b35f0bbf3051bd1c41b80f58b5d07c271d0dd04de2a4e", + "aura_public_key": "0x9cedc9f7b926191f64d68ee77dd90c834f0e73c0f53855d77d3b0517041d5640", + "grandpa_public_key": "0xde21d8171821fc29a43a1ed90ee75623edc3794012010f165b6afc3483a569aa", + "permissioned_candidate": true + } + }, + "governance_authority": { + "mainchain_address": "addr_test1vp7yrmz5p6mdm3ph0a0jaxtk58hny3wseuw80fwql93huygczalde", + "mainchain_key": "./secrets/substrate/devnet/keys/governance_authority/init.skey.json.decrypted" + }, + "selected_node": "dave", + "node": "${nodes_config[nodes][${nodes_config[selected_node]}]}", + "token_conversion_rate": 9, + "block_duration": 6, + "slots_in_epoch": 60, + "token_policy_id": "a8629ed63b21472af8b18382303a2367b4707e3c2bc852f303a4612b.4655454c" + } +} diff --git a/e2e/config/substrate/devnet_stack.json b/e2e/config/substrate/devnet_stack.json new file mode 100644 index 0000000000..4a9c3c043d --- /dev/null +++ b/e2e/config/substrate/devnet_stack.json @@ -0,0 +1,28 @@ +{ + "stack_config": { + "ogmios_host": "devnet-services-service", + "ogmios_port": 1337, + "tools_host": "10.0.12.65", + "ssh": { + "username": "root", + "host": "${stack_config[tools_host]}", + "port": 22, + "host_keys_path": "config/substrate/known_hosts", + "private_key_path": "secrets/substrate/devnet/keys/ssh-key.yaml.decrypted" + }, + "tools": { + "cardano_cli": { + "cli": "export CARDANO_NODE_SOCKET_PATH=/ipc/node.socket && /tools/cardano-cli", + "ssh": "${stack_config[ssh]}" + }, + "partner_chains_node": { + "cli": "/tools/devnet/partner-chains-node", + "ssh": "${stack_config[ssh]}" + }, + "bech32": { + "cli": "/tools/bech32", + "ssh": "${stack_config[ssh]}" + } + } + } +} diff --git a/e2e/config/substrate/known_hosts b/e2e/config/substrate/known_hosts new file mode 100644 index 0000000000..9afdaa3a06 --- /dev/null +++ b/e2e/config/substrate/known_hosts @@ -0,0 +1,3 @@ +binary-host-service.sc.svc.cluster.local ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINevlKvq/nBitkudwLWGB2mzuCWfsZGcBOhGLg/JvsLT +10.0.12.163 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINevlKvq/nBitkudwLWGB2mzuCWfsZGcBOhGLg/JvsLT +dave.node.sc.iog.io ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHsbZIlBE5vO7DcpSZvSpcwyGiEPVbIgwm8WJXteUgLG \ No newline at end of file diff --git a/e2e/config/substrate/local-ci.json b/e2e/config/substrate/local-ci.json new file mode 100644 index 0000000000..540155aeb4 --- /dev/null +++ b/e2e/config/substrate/local-ci.json @@ -0,0 +1,21 @@ +{ + "nodes_config": { + "nodes": { + "alice": { + "host": "partner-chains-node-1" + }, + "bob": { + "host": "partner-chains-node-2" + }, + "charlie": { + "host": "partner-chains-node-3" + }, + "dave": { + "host": "partner-chains-node-4" + }, + "eve": { + "host": "partner-chains-node-5" + } + } + } +} \ No newline at end of file diff --git a/e2e/config/substrate/local_nodes.json b/e2e/config/substrate/local_nodes.json new file mode 100644 index 0000000000..0ecd1f71a5 --- /dev/null +++ b/e2e/config/substrate/local_nodes.json @@ -0,0 +1,106 @@ +{ + "deployment_mc_epoch": 2, + "genesis_utxo": "f8fbe7316561e57de9ecd1c86ee8f8b512a314ba86499ba9a584bfa8fe2edc8d#0", + "main_chain": { + "network": "--testnet-magic 42", + "epoch_length": 120, + "slot_length": 1, + "active_slots_coeff": 0.4, + "security_param": 5, + "init_timestamp": 1742993000, + "block_stability_margin": 0 + }, + "nodes_config": { + "nodes": { + "alice": { + "host": "127.0.0.1", + "port": "9933", + "aura_ss58_address": "5Cyx94iyji8namhRxvs4mAbURtPsvwjWCb68ZihNzfRysGLZ", + "public_key": "0x0258dc1e341e42ba85b393804c1e8a531485ec3b73b2d5cd2b0bf56cbcaf102a7e", + "aura_public_key": "0x289c161586d774dda981fdb184d061a28e04bdf81322c545b9c37549e7412f2f", + "grandpa_public_key": "0x17edf4615819dbe1de4ed887ba0b438190923e14e25de9c6ff25320abcfe7d29", + "permissioned_candidate": true + }, + "bob": { + "host": "127.0.0.1", + "port": "9934", + "aura_ss58_address": "5E4op92Z2Di1GoVS9KqnoGVKQXG2R9x1vdh3RW892YLFsLrc", + "public_key": "0x030e0bcd9e16d770ade1fb5a5a50f2da012f1ea53713a9ba8aed5f009bc451404d", + "aura_public_key": "0x588c84949c703058577df51caaf82ca81cb1df43928600763a0050d16d2c210f", + "grandpa_public_key": "0xf39299bb0a1d6fab19b4990edaf435edb70f5755983bf63fce3ecd13a6be8ea5", + "permissioned_candidate": true + }, + "charlie": { + "host": "127.0.0.1", + "port": "9935", + "aura_ss58_address": "5CPCXHsSVaSRBMe8Ljs1HPrsuB8pJYuceoMT7TVKksM6uuj3", + "public_key": "0x03425892d705436ccfd24e0613ca7410e18eb142461579e3c908f4c5e4d429a90d", + "aura_public_key": "0x0e1aea53d513c3785ec8d02c2fb43eb98b14169f56194690cf1c8cb2d1639f3a", + "grandpa_public_key": "0xbfd485365f3765c31aa70502261868e79ca045d4d0f16db70865280e3a741f88", + "permissioned_candidate": true + }, + "dave": { + "host": "127.0.0.1", + "port": "9936", + "public_key": "0x039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180", + "aura_public_key": "0xe85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73", + "grandpa_public_key": "0xcdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vphpcf32drhhznv6rqmrmgpuwq06kug0lkg22ux777rtlqst2er0r", + "aura_ss58_address": "5HKLH5ErLMNHReWGFGtrDPRdNqdKP56ArQA6DFmgANzunK7A", + "keys_files": { + "cardano_payment_key": "/partner-chains-nodes/partner-chains-node-4/keys/payment.skey", + "spo_signing_key": "./secrets/substrate/local/keys/dave/cold.skey", + "spo_public_key": "./secrets/substrate/local/keys/dave/cold.vkey", + "partner_chain_signing_key": "./secrets/substrate/local/keys/dave/sidechain.skey" + } + }, + "eve": { + "host": "127.0.0.1", + "port": "9937", + "public_key": "0x0364b1f01f6e803be10abc6dd6fe08ced61cf3eaaef2dbdc72b4e774c4b6a564af", + "aura_ss58_address": "5G4gFEZJaCDQfXjef24P8oR6hpD7FKi3GNaHwtRL7hmTBxPC", + "aura_public_key": "0xb0eb82cbdf9f92c384d88ea14de34aa38f7d05b0131b7b9bc21bb3f395920c22", + "grandpa_public_key": "0x83c5eea08f80a64d7abf4c15a8a19d9f186aa62fc14389a7c6d6042bcc971daf", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vzzt5pwz3pum9xdgxalxyy52m3aqur0n43pcl727l37ggscl8h7v8", + "keys_files": { + "cardano_payment_key": "/partner-chains-nodes/partner-chains-node-5/keys/payment.skey", + "spo_signing_key": "./secrets/substrate/local/keys/eve/cold.skey", + "spo_public_key": "./secrets/substrate/local/keys/eve/cold.vkey", + "partner_chain_signing_key": "./secrets/substrate/local/keys/eve/sidechain.skey" + } + } + }, + "governance_authority": { + "mainchain_address": "addr_test1vr5vxqpnpl3325cu4zw55tnapjqzzx78pdrnk8k5j7wl72c6y08nd", + "mainchain_key": "/keys/funded_address.skey", + "mainchain_pub_key": "fc014cb5f071f5d6a36cb5a7e5f168c86555989445a23d4abec33d280f71aca4", + "mainchain_pub_key_hash": "0xe8c300330fe315531ca89d4a2e7d0c80211bc70b473b1ed4979dff2b" + }, + "additional_governance_authorities": [ + { + "mainchain_address": "addr_test1vphpcf32drhhznv6rqmrmgpuwq06kug0lkg22ux777rtlqst2er0r", + "mainchain_key": "/keys/owner-utxo.skey", + "mainchain_pub_key_hash": "6e1c262a68ef714d9a18363da03c701fab710ffd90a570def786bf82" + } + ], + "selected_node": "alice", + "node": "${nodes_config[nodes][${nodes_config[selected_node]}]}", + "token_conversion_rate": 9, + "block_duration": 6, + "slots_in_epoch": 5, + "token_policy_id": "ba8b181cdf7fb639fa3d67e9b514cc685e7603ee19c72baf5b1f6d2a.4655454c", + "d_param_min": { + "permissioned_candidates_number": 1, + "trustless_candidates_number": 1 + }, + "d_param_max": { + "permissioned_candidates_number": 3, + "trustless_candidates_number": 2 + } + }, + "timeouts": { + "main_chain_tx": 20 + } +} diff --git a/e2e/config/substrate/local_stack.json b/e2e/config/substrate/local_stack.json new file mode 100644 index 0000000000..06c190d952 --- /dev/null +++ b/e2e/config/substrate/local_stack.json @@ -0,0 +1,21 @@ +{ + "stack_config": { + "ogmios_scheme": "ws", + "ogmios_host": "ogmios", + "ogmios_port": 1337, + "tools_shell": "/bin/bash", + "tools": { + "cardano_cli": { + "cli": "cardano-cli", + "shell": "docker exec cardano-node-1 bash -c" + }, + "partner_chains_node": { + "cli": "./partner-chains-node", + "shell": "docker exec partner-chains-setup bash -c" + }, + "bech32": { + "cli": "bech32" + } + } + } +} \ No newline at end of file diff --git a/e2e/config/substrate/staging-ci.json b/e2e/config/substrate/staging-ci.json new file mode 100644 index 0000000000..0ecf9d6f28 --- /dev/null +++ b/e2e/config/substrate/staging-ci.json @@ -0,0 +1,29 @@ +{ + "nodes_config": { + "nodes": { + "validator-1": { + "host": "staging-preview-validator-1-service.staging-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-2": { + "host": "staging-preview-validator-2-service.staging-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-3": { + "host": "staging-preview-validator-3-service.staging-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-4": { + "host": "staging-preview-validator-4-service.staging-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + }, + "validator-5": { + "host": "staging-preview-validator-5-service.staging-preview.svc.cluster.local", + "port": "${nodes_config[default_port]}" + } + } + }, + "stack_config": { + "tools_host": "binary-host-service.sc.svc.cluster.local" + } +} \ No newline at end of file diff --git a/e2e/config/substrate/staging_nodes.json b/e2e/config/substrate/staging_nodes.json new file mode 100644 index 0000000000..b9007eeb99 --- /dev/null +++ b/e2e/config/substrate/staging_nodes.json @@ -0,0 +1,115 @@ +{ + "deployment_mc_epoch": 867, + "initial_pc_epoch": 4837864, + "genesis_utxo": "de7b8efb08243b770be30cfa22a9ed64317c50052bb4394392960ddcc9810048#1", + "deployment_version": "v1.7.0", + "test_environment": "staging", + "nodes_config": { + "nodes": { + "validator-1": { + "host": "10.0.10.228", + "port": "30321", + "aura_ss58_address": "5GHLr2zBDNPXno9XdKgh541uRSiTxnZyzFcr4jK6HUbHMpit", + "pool_id": "da74fc8256d15c7ab3370a6ca28398986cb97c32e9ef66026ac61e99", + "public_key": "0x03b827f4da9711bab7292e5695576a841a4d20af9a07b1ba7a230168d2a78e9df4", + "aura_public_key": "0xba94651de6279a38a416b97b9720c3df76224435e951ac73e9e302a4ee9fcf73", + "grandpa_public_key": "0xdde2501588713ddad5daf5a898c19d82cd591609c9184679868640c8cfe8287d", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vq6ywn0f007x32j47jrk5qy9hy3gknsvszrcpdqkeaye7pshu2w2t", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-1/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-1/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-1/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-1/partner_chain.skey.json.decrypted" + } + }, + "validator-2": { + "host": "10.0.10.228", + "port": "30322", + "aura_ss58_address": "5DHbxU687f1Y3x8yBCWMtqSiJ5qt2yrxQPXNXZNNDaFtmXKv", + "pool_id": "eaed153a8b046770cfd094ee72d080ea682188e63ac11937e3f7f827", + "public_key": "0x02ef5bcd94d54a18ad199559782cd72ac3ccd850976aaaafbca8f9d2625afbf7c4", + "aura_public_key": "0x36128fff2acc04f206ccaf4e9f8e9995998efced29075a58b7d76d3735c21208", + "grandpa_public_key": "0x8f9a9856a27cc114ce85b64f41144f0c907c4bd8b3102b083b52b6b61aff6c47", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vz80jkz5d2kdaykfrqlqryr6lt46tunk4ldfjqv88hevrpsrdfmm5", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-2/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-2/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-2/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-2/partner_chain.skey.json.decrypted" + } + }, + "validator-3": { + "host": "10.0.10.228", + "port": "30323", + "aura_ss58_address": "5FYtL6HccYhk6KZeFP7hNnkMaXrAwVpTHJWsfnNJcu8AM6in", + "pool_id": "7dfba85597a867fffa59037df7f6adcd50e745dcceac2b48eda94b20", + "public_key": "0x02f2762ab6e1a125dc03908a7b738f8023d13763f28a11d7633c6c8bc463478430", + "aura_public_key": "0x9a32d3896a56e822321f7bc915befc8ce112c5d67e3c6497295bd3d7b020f94c", + "grandpa_public_key": "0x4f3c0ecc6dc474f27ad7967f5cdbd50da047ffedbc91b65f5cd247515489c98f", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vrxc6wvtmqrqx4n9e89sqf9u0w0jcp8a96s7gkddfsr0ppcdvzcf6", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-3/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-3/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-3/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-3/partner_chain.skey.json.decrypted" + } + }, + "validator-4": { + "host": "10.0.10.228", + "port": "30324", + "aura_ss58_address": "5GVpqdtqjxqUjuVKMkmh8ehSwcs2nXjpvzHqjouZXMJAyC4b", + "pool_id": "2a3f5dd02da1310e081f2367412e02b72baad3e2a5045f62df2c78c5", + "public_key": "0x025e19f82c5e2bac5e8869d49ff26359e442628bc5cfa38eeb5275f43d04015da8", + "aura_public_key": "0xc41992b8eb2f3a8a6c46211df584827f9eeb0175e2c75e1242392262b55b6874", + "grandpa_public_key": "0x34b71fdad96431bf115350d8ad21eec07a2b154ff32dc31125f988e308bebea8", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vq65mgmcpd6rq6ndy22kwxzdc9u0tmrpr7s30037qdhlalg3f5ax0", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-4/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-4/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-4/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-4/partner_chain.skey.json.decrypted" + } + }, + "validator-5": { + "host": "10.0.10.228", + "port": "30325", + "aura_ss58_address": "5DsfhT7HJe6i5LYeKBzefrXijW5UgPsn2Cuyw5WMa4uEktTn", + "pool_id": "ae81beee7a6c3fa13bba811f91f63ebdd7eb25dd8a62476d4996de10", + "public_key": "0x03f38a062a4b372c045c1dddc4fe98a2c9cb1d6eec8bf02f973fd29b1096cd8155", + "aura_public_key": "0x500d7ff6d903c85db5ee5624df9510c2a085cf30da260166bd370010d0bdc97a", + "grandpa_public_key": "0xa04d74c1539550876d04e4d2de4e0531087c3b6810ce96ddc16d78ccf4ac4f11", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vqqm36dcg5xeeqgc29r8gu6wujlmr4fyrdrvuzq4qnem2vs7g2kvj", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/staging/keys/validator-5/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/staging/keys/validator-5/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/staging/keys/validator-5/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/staging/keys/validator-5/partner_chain.skey.json.decrypted" + } + } + }, + "governance_authority": { + "mainchain_address": "addr_test1vq0sjaaupatuvl9x6aefdsd4whlqtfku93068qzkhf3u2rqt9cnuq", + "mainchain_key": "./secrets/substrate/staging/keys/governance_authority/init.skey.json.decrypted", + "mainchain_pub_key": "a90ba46b07c0c2c940e3e3149aee5817130a1d8b600bc85b91345e4f7a1e5aca" + }, + "selected_node": "validator-4", + "node": "${nodes_config[nodes][${nodes_config[selected_node]}]}", + "token_conversion_rate": 9, + "block_duration": 6, + "slots_in_epoch": 60, + "token_policy_id": "", + "d_param_min": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 + }, + "d_param_max": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 + } + } +} diff --git a/e2e/config/substrate/staging_stack.json b/e2e/config/substrate/staging_stack.json new file mode 100644 index 0000000000..adefac2ca9 --- /dev/null +++ b/e2e/config/substrate/staging_stack.json @@ -0,0 +1,28 @@ +{ + "stack_config": { + "ogmios_host": "staging-preview-services-service.staging-preview.svc.cluster.local", + "ogmios_port": 1337, + "tools_host": "10.0.12.163", + "ssh": { + "username": "root", + "host": "${stack_config[tools_host]}", + "port": 22, + "host_keys_path": "config/substrate/known_hosts", + "private_key_path": "secrets/substrate/staging/keys/ssh-key.yaml.decrypted" + }, + "tools": { + "cardano_cli": { + "cli": "export CARDANO_NODE_SOCKET_PATH=/ipc/node.socket && /tools/cardano-cli", + "ssh": "${stack_config[ssh]}" + }, + "partner_chains_node": { + "cli": "/tools/staging/partner-chains-node-latest", + "ssh": "${stack_config[ssh]}" + }, + "bech32": { + "cli": "/tools/bech32", + "ssh": "${stack_config[ssh]}" + } + } + } +} diff --git a/e2e/config/substrate/uat_preview_nodes.json b/e2e/config/substrate/uat_preview_nodes.json new file mode 100644 index 0000000000..e5045ba046 --- /dev/null +++ b/e2e/config/substrate/uat_preview_nodes.json @@ -0,0 +1,128 @@ +{ + "deployment_mc_epoch": 890, + "initial_pc_epoch": 4843200, + "genesis_utxo": "3b715a225582eae835c87d13d28fc4ea0366db8acecd13d9d80d1623d5aefd5e#1", + "deployment_version": "v1.6.0", + "test_environment": "uat_preview", + "nodes_config": { + "nodes": { + "alice": { + "host": "alice.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5DU2xsSHPigU2md6r3rSU9kKAuQvHyJLS5Xsq2pVt18K9AK8", + "pool_id": "da74fc8256d15c7ab3370a6ca28398986cb97c32e9ef66026ac61e99", + "public_key": "0x03046279673f9e241478a577b91c798ebee4c6087f9d9ddd22cee7e059166dbf4a", + "aura_public_key": "0x3e073288910089cd56e725e478b616ed753b694e8bcca3a2a5ba1b40eb72ca4e", + "grandpa_public_key": "0xd9d0a86780aeefecb026e1bef06c85fc8609fb833cc62de3df03fdb43e7e4b2a", + "rotation_candidate": true, + "cardano_payment_addr": "addr_test1vq6ywn0f007x32j47jrk5qy9hy3gknsvszrcpdqkeaye7pshu2w2t", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/uat/keys/validator-1/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/uat/keys/validator-1/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/uat/keys/validator-1/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/uat/keys/validator-1/partner_chain.skey.json.decrypted" + } + }, + "bob": { + "host": "bob.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5CAWrgrjdwURGdRManWLgun2oLtyC6LtRwRCvVe4zh8ap3Cw", + "pool_id": "eaed153a8b046770cfd094ee72d080ea682188e63ac11937e3f7f827", + "public_key": "0x034f94750f274bbfb38f38d931f009d321acd56c8bcbe5c17eff34ee186a0277cf", + "aura_public_key": "0x046e71b9ef1631574426c173d5f8b2ba4bbc6a3680c9cf3502710fe84891301a", + "grandpa_public_key": "0x98853817df07cb4be9f3f76429d09e01eac35b18c3d777e663ccc89e3012dd64", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vz80jkz5d2kdaykfrqlqryr6lt46tunk4ldfjqv88hevrpsrdfmm5", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/uat/keys/validator-2/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/uat/keys/validator-2/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/uat/keys/validator-2/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/uat/keys/validator-2/partner_chain.skey.json.decrypted" + } + }, + "charlie": { + "host": "charlie.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5H986Epy9e8MHL3617TthaoRfvANu9uLvfKjp3g3e4Aajj4M", + "pool_id": "7dfba85597a867fffa59037df7f6adcd50e745dcceac2b48eda94b20", + "public_key": "0x024f0ff7822ddd402cb1da57af91d6e5d7ca70fa3d89d96e31a465e8078ace764f", + "aura_public_key": "0xe08bba4b24e9f986ab06e51d9bb9e4ab767eddc27aa25a86bc7a9edc438c517b", + "grandpa_public_key": "0x8b99d6737bd58e06b61bdd07d71abc235101b3c8860f6ad5cc7f27d4ce5b3a58", + "permissioned_candidate": true, + "cardano_payment_addr": "addr_test1vrxc6wvtmqrqx4n9e89sqf9u0w0jcp8a96s7gkddfsr0ppcdvzcf6", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/uat/keys/validator-3/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/uat/keys/validator-3/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/uat/keys/validator-3/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/uat/keys/validator-3/partner_chain.skey.json.decrypted" + } + }, + "dave": { + "host": "dave.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5GVpqdtqjxqUjuVKMkmh8ehSwcs2nXjpvzHqjouZXMJAyC4b", + "pool_id": "2a3f5dd02da1310e081f2367412e02b72baad3e2a5045f62df2c78c5", + "public_key": "0x025e19f82c5e2bac5e8869d49ff26359e442628bc5cfa38eeb5275f43d04015da8", + "aura_public_key": "0xc41992b8eb2f3a8a6c46211df584827f9eeb0175e2c75e1242392262b55b6874", + "grandpa_public_key": "0x34b71fdad96431bf115350d8ad21eec07a2b154ff32dc31125f988e308bebea8", + "cardano_payment_addr": "addr_test1vq65mgmcpd6rq6ndy22kwxzdc9u0tmrpr7s30037qdhlalg3f5ax0", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/uat/keys/validator-4/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/uat/keys/validator-4/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/uat/keys/validator-4/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/uat/keys/validator-4/partner_chain.skey.json.decrypted" + } + }, + "eve": { + "host": "eve.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5DsfhT7HJe6i5LYeKBzefrXijW5UgPsn2Cuyw5WMa4uEktTn", + "pool_id": "ae81beee7a6c3fa13bba811f91f63ebdd7eb25dd8a62476d4996de10", + "public_key": "0x03f38a062a4b372c045c1dddc4fe98a2c9cb1d6eec8bf02f973fd29b1096cd8155", + "aura_public_key": "0x500d7ff6d903c85db5ee5624df9510c2a085cf30da260166bd370010d0bdc97a", + "grandpa_public_key": "0xa04d74c1539550876d04e4d2de4e0531087c3b6810ce96ddc16d78ccf4ac4f11", + "cardano_payment_addr": "addr_test1vqqm36dcg5xeeqgc29r8gu6wujlmr4fyrdrvuzq4qnem2vs7g2kvj", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/uat/keys/validator-5/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/uat/keys/validator-5/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/uat/keys/validator-5/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/uat/keys/validator-5/partner_chain.skey.json.decrypted" + } + }, + "ferdie": { + "host": "ferdie.node.sc.iog.io", + "port": "9944", + "aura_ss58_address": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "pool_id": "9ad70551d17317e44866850f9827f6464f0867e50bff5a87b1f9e3c0", + "public_key": "0x0291f1217d5a04cb83312ee3d88a6e6b33284e053e6ccfc3a90339a0299d12967c", + "aura_public_key": "0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c", + "grandpa_public_key": "0x568cb4a574c6d178feb39c27dfc8b3f789e5f5423e19c71633c748b9acf086b5", + "cardano_payment_addr": "addr_test1vrkp4j33xcysqfancc7fttdrwf86ztgpaqhhzu98vahnevqfng7r9", + "keys_files": { + "cardano_payment_key": "./secrets/substrate/devnet/keys/ferdie/payment.skey.json.decrypted", + "spo_signing_key": "./secrets/substrate/devnet/keys/ferdie/cold.skey.json.decrypted", + "spo_public_key": "./secrets/substrate/devnet/keys/ferdie/cold.vkey.json.decrypted", + "partner_chain_signing_key": "./secrets/substrate/devnet/keys/ferdie/partner_chain.skey.json.decrypted" + } + } + }, + "governance_authority": { + "mainchain_address": "addr_test1vq0sjaaupatuvl9x6aefdsd4whlqtfku93068qzkhf3u2rqt9cnuq", + "mainchain_key": "./secrets/substrate/uat/keys/governance_authority/init.skey.json.decrypted" + }, + "selected_node": "dave", + "node": "${nodes_config[nodes][${nodes_config[selected_node]}]}", + "token_conversion_rate": 9, + "block_duration": 6, + "slots_in_epoch": 60, + "token_policy_id": "", + "d_param_min": { + "permissioned_candidates_number": 2, + "trustless_candidates_number": 1 + }, + "d_param_max": { + "permissioned_candidates_number": 2, + "trustless_candidates_number": 1 + } + } +} diff --git a/e2e/config/substrate/uat_preview_stack.json b/e2e/config/substrate/uat_preview_stack.json new file mode 100644 index 0000000000..cd061bffa7 --- /dev/null +++ b/e2e/config/substrate/uat_preview_stack.json @@ -0,0 +1,28 @@ +{ + "stack_config": { + "ogmios_host": "dave.node.sc.iog.io", + "ogmios_port": 1337, + "tools_host": "dave.node.sc.iog.io", + "ssh": { + "username": "root", + "host": "${stack_config[tools_host]}", + "port": 22, + "host_keys_path": "config/substrate/known_hosts", + "private_key_path": "secrets/substrate/uat_preview/keys/ssh-key.yaml.decrypted" + }, + "tools": { + "cardano_cli": { + "cli": "export CARDANO_NODE_SOCKET_PATH=/run/cardano-node/node.socket && cardano-cli", + "ssh": "${stack_config[ssh]}" + }, + "partner_chains_node": { + "cli": "/tools/staging/partner-chains-node-latest", + "ssh": "${stack_config[ssh]}" + }, + "bech32": { + "cli": "/tools/bech32", + "ssh": "${stack_config[ssh]}" + } + } + } +} diff --git a/e2e/src/apiconfig.rs b/e2e/src/apiconfig.rs new file mode 100644 index 0000000000..d8fe8c77e6 --- /dev/null +++ b/e2e/src/apiconfig.rs @@ -0,0 +1,125 @@ +use serde::Deserialize; +use serde_json::Value as JsonValue; + +#[derive(Debug, Clone, Deserialize)] +pub struct Reserve { + pub token_name: String, + pub v_function_script_path: String, + pub v_function_updated_script_path: String, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Tool { + pub cli: String, + pub shell: Option, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Tools { + pub cardano_cli: Tool, + pub partner_chains_node: Tool, + pub bech32: Tool, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct MainChainConfig { + pub network: String, + pub epoch_length: i64, + pub slot_length: i64, + pub active_slots_coeff: f64, + pub security_param: i64, + pub init_timestamp: i64, + pub block_stability_margin: i64, +} +#[derive(Debug, Clone, Deserialize)] +pub struct MainchainAccount { + pub mainchain_key: String, + pub mainchain_pub_key: Option, + pub mainchain_address: String, +} +#[derive(Debug, Clone, Deserialize)] +pub struct NodesApiConfig { + pub governance_authority: MainchainAccount, + pub reserve: Option, + pub additional_governance_authorities: Option>, +} +#[derive(Debug, Clone, Deserialize)] +pub struct StackApiConfig { + pub ogmios_scheme: Option, + pub ogmios_host: String, + pub ogmios_port: u64, + pub tools: Tools, +} +impl StackApiConfig { + pub fn ogmios_url(&self) -> String { + format!( + "{}://{}:{}", + self.ogmios_scheme.clone().unwrap_or("http".to_string()), + self.ogmios_host, + self.ogmios_port + ) + } +} +#[derive(Debug, Clone, Deserialize)] +pub struct Timeout { + pub long_running_function: u64, + pub register_cmd: u64, + pub deregister_cmd: u64, + pub main_chain_tx: u64, +} +#[derive(Debug, Clone, Deserialize)] +pub struct ApiConfig { + pub genesis_utxo: String, + pub nodes_config: NodesApiConfig, + pub stack_config: StackApiConfig, + pub main_chain: MainChainConfig, + pub timeouts: Timeout, +} +impl ApiConfig { + pub fn load() -> Self { + let cwd = std::env::current_dir().expect("cwd exists"); + let cwd = cwd.to_str().expect("msg"); + let blockchain = "substrate"; + let nodes_env = "local"; + type JsonObj = serde_json::Map; + fn load_json_obj(path: &str) -> JsonObj { + serde_json::from_str::(&std::fs::read_to_string(path).unwrap()) + .unwrap() + .as_object() + .unwrap() + .clone() + } + // It would be nicer not to copy the whole config folder but the config jsons reference the + // location of the v-function scripts in the file system and it was not worth doing the + // work to deal with it. + let default_config = load_json_obj(&format!("{cwd}/config/config.json")); + let blockchain_config_path = + load_json_obj(&format!("{cwd}/config/{blockchain}/{nodes_env}_nodes.json")); + let stack_config_path = + load_json_obj(&format!("{cwd}/config/{blockchain}/{nodes_env}_stack.json")); + + fn merge(objs: &[JsonObj]) -> JsonObj { + let mut res = JsonObj::new(); + for obj in objs { + for (k, v) in obj { + res.entry(k) + .and_modify(|ev| { + if ev.is_object() && v.is_object() { + let ev_obj = ev.as_object().unwrap().clone(); + let v = v.as_object().unwrap().clone(); + *ev = JsonValue::Object(merge(&[ev_obj, v])); + } else { + *ev = v.clone(); + } + }) + .or_insert(v.clone()); + } + } + res + } + // The original code uses Omegaconf which does a thing with merging jsons before filling up the + // config class fields with data. I replicated this behavior here. + let combined = merge(&[default_config, blockchain_config_path, stack_config_path]); + serde_json::from_value::(JsonValue::Object(combined)).unwrap() + } +} diff --git a/e2e/src/blockchain_api.rs b/e2e/src/blockchain_api.rs new file mode 100644 index 0000000000..5ae951df52 --- /dev/null +++ b/e2e/src/blockchain_api.rs @@ -0,0 +1,400 @@ +use serde_json::Value as JsonValue; +use std::collections::HashMap; + +use crate::{apiconfig::*, run_command::*}; + +fn parse_json_response(result: String) -> Result { + serde_json::from_str(&result).map_err(|e| e.to_string()) +} + +pub fn uuid4() -> String { + uuid::Uuid::new_v4().to_string() +} +pub struct PartnerChainsNode { + pub config: ApiConfig, + pub cli: String, + pub run_command: Runner, +} +// The original code groups pc node subcommands into classes. I've done away with that. +impl PartnerChainsNode { + pub fn new(config: &ApiConfig) -> Self { + let cli_config = config.stack_config.tools.partner_chains_node.clone(); + let cli = cli_config.cli.clone(); + let run_command = Runner::new(cli_config.shell.unwrap().clone()); + + Self { config: config.clone(), cli, run_command } + } + + pub fn get_scripts(&self) -> Result { + let cmd = format!( + "{} smart-contracts get-scripts + --genesis-utxo {} + --ogmios-url {}", + self.cli, + self.config.genesis_utxo, + self.config.stack_config.ogmios_url() + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + parse_json_response(response) + } + + pub fn reserve_init(&self, payment_key: &str) -> Result { + let cli = &self.cli; + let genesis_utxo = &self.config.genesis_utxo; + let ogmios_url = &self.config.stack_config.ogmios_url(); + let cmd = format!( + "{cli} smart-contracts reserve init + --payment-key-file {payment_key} + --genesis-utxo {genesis_utxo} + --ogmios-url {ogmios_url}" + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + let parsed_response = parse_json_response(response)?; + return self.handle_governance_signature(parsed_response); + } + + pub fn reserve_create( + &self, + v_function_hash: &str, + initial_deposit: i64, + token: &str, + payment_key: &str, + ) -> Result { + let cli = &self.cli; + let genesis_utxo = &self.config.genesis_utxo; + let ogmios_url = &self.config.stack_config.ogmios_url(); + let cmd = format!( + "{cli} smart-contracts reserve create + --total-accrued-function-script-hash {v_function_hash} + --initial-deposit-amount {initial_deposit} + --token {token} + --payment-key-file {payment_key} + --genesis-utxo {genesis_utxo} + --ogmios-url {ogmios_url}" + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + let parsed_response = parse_json_response(response)?; + return self.handle_governance_signature(parsed_response); + } + + pub fn reserve_handover(&self, payment_key: &str) -> Result { + let cli = &self.cli; + let genesis_utxo = &self.config.genesis_utxo; + let ogmios_url = &self.config.stack_config.ogmios_url(); + let cmd = format!( + "{cli} smart-contracts reserve handover + --payment-key-file {payment_key} + --genesis-utxo {genesis_utxo} + --ogmios-url {ogmios_url}" + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + let parsed_response = parse_json_response(response)?; + return self.handle_governance_signature(parsed_response); + } + + pub fn reserve_deposit(&self, amount: i64, payment_key: &str) -> Result { + let cli = &self.cli; + let genesis_utxo = &self.config.genesis_utxo; + let ogmios_url = &self.config.stack_config.ogmios_url(); + let cmd = format!( + "{cli} smart-contracts reserve deposit + --amount {amount} + --payment-key-file {payment_key} + --genesis-utxo {genesis_utxo} + --ogmios-url {ogmios_url}" + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + let parsed_response = parse_json_response(response)?; + return self.handle_governance_signature(parsed_response); + } + + pub fn reserve_release( + &self, + reference_utxo: &str, + amount: i64, + payment_key: &str, + ) -> Result { + let cli = &self.cli; + let genesis_utxo = &self.config.genesis_utxo; + let ogmios_url = &self.config.stack_config.ogmios_url(); + let cmd = format!( + "{cli} smart-contracts reserve release + --reference-utxo {reference_utxo} + --amount {amount} + --payment-key-file {payment_key} + --genesis-utxo {genesis_utxo} + --ogmios-url {ogmios_url}" + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + let parsed_response = parse_json_response(response)?; + return self.handle_governance_signature(parsed_response); + } + + pub fn sign_tx( + &self, + transaction_cbor: &str, + payment_key: &str, + ) -> Result, String> { + let cmd = format!( + "{} smart-contracts sign-tx + --transaction {transaction_cbor} + --payment-key-file {payment_key} ", + self.cli, + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + parse_json_response(response)? + .as_object() + .ok_or("expected obj".to_string()) + .cloned() + } + + pub fn assemble_and_submit_tx( + &self, + transaction_cbor: &str, + witnesses: &[String], + ) -> Result { + let witnesses_str = witnesses.join(" "); + let cmd = format!( + "{} smart-contracts assemble-and-submit-tx + --transaction {transaction_cbor} + --witnesses {witnesses_str} + --ogmios-url {}", + self.cli, + self.config.stack_config.ogmios_url() + ); + let response = self.run_command.run(&cmd, self.config.timeouts.main_chain_tx)?; + parse_json_response(response) + } + + fn handle_governance_signature(&self, response: JsonValue) -> Result { + fn contains_key(data: &JsonValue, key: &str) -> bool { + match data { + JsonValue::Object(map) => map.contains_key(key), + JsonValue::Array(values) => { + values.iter().any(|v| v.as_object().map_or(false, |o| o.contains_key(key))) + }, + _ => false, + } + } + + if contains_key(&response, "transaction_to_sign") { + self.handle_multisig(response) + } else { + Ok(response) + } + } + + fn handle_multisig(&self, response: JsonValue) -> Result { + fn sign_and_submit_tx( + pcnode: &PartnerChainsNode, + tx_cbor: &str, + config: &ApiConfig, + ) -> Result { + let mut witnesses = Vec::new(); + for authority in config + .nodes_config + .additional_governance_authorities + .clone() + .unwrap_or_default() + { + let witness = pcnode.sign_tx(tx_cbor, &authority.mainchain_key)?; + witnesses + .push(witness["cborHex"].as_str().map(|x| x.to_owned()).ok_or("expected str")?); + } + pcnode.assemble_and_submit_tx(tx_cbor, &witnesses) + } + + let mut result = JsonValue::Null; + + match response { + JsonValue::Array(arr) => { + for tx in arr { + let tx_cbor = tx["transaction_to_sign"]["tx"]["cborHex"] + .as_str() + .ok_or("expected str")?; + result = sign_and_submit_tx(&self, tx_cbor, &self.config)? + } + }, + JsonValue::Object(obj) => { + let tx_cbor = + obj["transaction_to_sign"]["tx"]["cborHex"].as_str().ok_or("expected str")?; + result = sign_and_submit_tx(&self, tx_cbor, &self.config)? + }, + _ => {}, + } + Ok(result) + } +} +pub struct CardanoCli { + pub cli: String, + pub network: String, + pub run_command: Runner, +} +impl CardanoCli { + pub fn new(config: &MainChainConfig, cardano_cli: &Tool) -> Self { + Self { + cli: cardano_cli.cli.clone(), + network: config.network.clone(), + run_command: Runner::new(cardano_cli.shell.as_ref().unwrap().clone()), + } + } + pub(crate) fn generate_payment_keys(&self) -> Result<(JsonValue, JsonValue), String> { + log::debug!("Generating payment keys..."); + let cmd = format!( + "{} latest address key-gen --verification-key-file /dev/stdout --signing-key-file /dev/stdout", + self.cli + ); + let result = self.run_command.run(&cmd, 120)?; + + let valid_json_string = format!("[{}]", result.replace("\r", "").replace("}\n{", "},\n{")); + let skey_vkey_pair = + serde_json::from_str::(&valid_json_string).map_err(|e| e.to_string())?; + let signing_key = skey_vkey_pair[0].clone(); + let verification_key = skey_vkey_pair[1].clone(); + log::debug!("Payment signing key: {signing_key}"); + log::debug!("Payment verification key: {verification_key}"); + Ok((signing_key, verification_key)) + } + pub fn build_address(&self, payment_vkey: &str) -> Result { + log::debug!("Building address..."); + let cmd = format!( + "{} latest address build --payment-verification-key {} {}", + self.cli, payment_vkey, self.network + ); + self.run_command.run(&cmd, 120).map(|r| r.trim().to_string()) + } + pub fn get_policy_id(&self, script_file: &str) -> Result { + log::debug!("Calculating policy id..."); + let cmd = format!("{} latest transaction policyid --script-file {}", self.cli, script_file); + self.run_command.run(&cmd, 120).map(|r| r.trim().to_string()) + } + pub fn build_tx_with_reference_script( + &self, + tx_in: &str, + address: &str, + lovelace: i64, + reference_script_file: &str, + change_address: &str, + ) -> Result<(String, String), String> { + log::debug!("Building transaction with reference script..."); + let raw_tx_filepath = format!("/tmp/reference_script_tx_{}.raw", uuid4()); + let cmd = format!( + "{} latest transaction build + --tx-in {tx_in} + --tx-out '{address}+{lovelace}' + --tx-out-reference-script-file {reference_script_file} + --change-address {change_address} + --out-file {raw_tx_filepath} + {}", + self.cli, self.network + ); + Ok((self.run_command.run(&cmd, 120)?, raw_tx_filepath)) + } + pub fn get_utxos( + &self, + address: &str, + ) -> Result, String> { + let cmd = format!( + "{} latest query utxo --address {address} {} --out-file /dev/stdout", + self.cli, self.network + ); + let result = self.run_command.run(&cmd, 120)?; + let map: JsonValue = serde_json::from_str(&result).map_err(|e| e.to_string())?; + Ok(map.as_object().expect("map").clone()) + } + + pub fn sign_transaction(&self, tx_filepath: &str, signing_key: &str) -> Result { + log::debug!("Signing transaction..."); + let signed_tx_filepath = format!("/tmp/signed_tx_{}.signed", uuid4()); + let cmd = format!( + "{} latest transaction sign + --tx-body-file {tx_filepath} + --signing-key-file {signing_key} + --out-file {signed_tx_filepath} + {}", + self.cli, self.network + ); + let result = self.run_command.run(&cmd, 120)?; + Ok(signed_tx_filepath) + } + + pub fn submit_transaction(&self, signed_tx_filepath: &str) -> Result { + log::debug!("Submitting transaction..."); + let cmd = format!( + "{} latest transaction submit --tx-file {signed_tx_filepath} {}", + self.cli, self.network + ); + self.run_command.run(&cmd, 120) + } + + pub fn get_address_key_hash( + &self, + payment_vkey: &str, + ) -> Result { + log::debug!("Getting address key hash..."); + let cmd = format!( + "{} latest address key-hash --payment-verification-key {payment_vkey}", + self.cli + ); + self.run_command.run(&cmd, 120) + } + + pub fn get_token_list_from_address( + &self, + address: &str, + ) -> Result, String> { + log::debug!("Getting list of tokens and ADA with amounts..."); + let utxos_json = self.get_utxos(address)?; + let mut tokens: HashMap = HashMap::new(); + for utxo in utxos_json.keys() { + for token_policy in utxos_json[utxo]["value"].as_object().expect("obj").keys() { + if token_policy == "lovelace" { + *tokens.entry("ADA".to_string()).or_default() += + utxos_json[utxo]["value"][token_policy].as_i64().expect("i64"); + } else { + for token_name in + utxos_json[utxo]["value"][token_policy].as_object().expect("obj").keys() + { + let token = format!("{token_policy}.{token_name}"); + *tokens.entry(token).or_default() += utxos_json[utxo]["value"] + [token_policy][token_name] + .as_i64() + .expect("i64"); + } + } + } + } + Ok(tokens) + } +} +pub struct SubstrateApi { + cardano_cli: CardanoCli, + partner_chains_node: PartnerChainsNode, +} +impl SubstrateApi { + pub fn new(config: &ApiConfig) -> Self { + Self { + cardano_cli: CardanoCli::new( + &config.main_chain, + &config.stack_config.tools.cardano_cli, + ), + partner_chains_node: PartnerChainsNode::new(config), + } + } +} +impl SubstrateApi { + pub fn cardano_cli(&self) -> &CardanoCli { + &self.cardano_cli + } + + pub fn partner_chains_node(&self) -> &PartnerChainsNode { + &self.partner_chains_node + } + + pub fn get_mc_balance(&self, address: &str, policy_id: &str) -> Result { + let tokens_dict = self.cardano_cli().get_token_list_from_address(address)?; + let balance = tokens_dict.get(policy_id).cloned().unwrap_or_default(); + log::debug!("MC address {address} balance: {balance} {policy_id}"); + Ok(balance) + } +} diff --git a/e2e/src/conftest.rs b/e2e/src/conftest.rs new file mode 100644 index 0000000000..19ff7d315f --- /dev/null +++ b/e2e/src/conftest.rs @@ -0,0 +1,44 @@ + +use std::time::Duration; + +use crate::blockchain_api::*; +use crate::run_command::*; +use serde_json::Value as JsonValue; + +pub fn write_file(runner: &Runner, content: &str) -> Result { + // TODO clean up temp files + let filepath = format!("/tmp/{}", uuid4()); + runner.run(&format!("echo '{content}' > {filepath}"), 120)?; + Ok(filepath) +} + +pub fn wait_until( + description: &str, + condition: F, + timeout: u64, + poll_interval: u64, +) -> Result +where + F: Fn() -> Option, +{ + let start = std::time::SystemTime::now(); + log::info!("WAIT UNTIL: {description}. TIMEOUT: {timeout}, POLL_INTERVAL: {poll_interval}"); + while std::time::SystemTime::now().duration_since(start).map_err(|e| e.to_string())? + < Duration::from_secs(timeout) + { + if let Some(result) = condition() { + return Ok(result); + } else { + std::thread::sleep(Duration::from_secs(poll_interval)); + } + } + Err(format!("WAIT UNTIL function TIMED OUT after {timeout}s on {description}.")) +} + +pub fn get_scripts(api: &SubstrateApi) -> JsonValue { + api.partner_chains_node().get_scripts().unwrap() +} + +pub fn addresses(api: &SubstrateApi) -> JsonValue { + api.partner_chains_node().get_scripts().unwrap()["addresses"].clone() +} diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs new file mode 100644 index 0000000000..14243dac14 --- /dev/null +++ b/e2e/src/lib.rs @@ -0,0 +1,21 @@ +#![cfg(test)] +/// Experiment porting our pytest E2E tests into Rust +/// +/// To run, start local-env and run `cargo test --bin e2e` +/// +/// For now I've implemented TestDepositFunds from e2e-tests/tests/reserve/test_reserve_management.py +/// +/// I tried to follow the structure of the Python code to a certain extent. +/// Fixtures are kept to their own functions but are passed by hand. +/// Tried to keep things simple for this initial prototype. +/// One thing to consider is to abandon the pythonic classy approach to CardanoCli and instead have command +/// calls as regular functions taking a context object containing all config data. +/// We might also want to not run the e2e tests through `cargo test` for better configurability and logging. +/// +/// Rust crate `rstest` seems to be the standard for test fixtures in Rust, but I didn't feel the need +/// to start using it yet. +mod apiconfig; +mod blockchain_api; +mod conftest; +mod reserve; +mod run_command; diff --git a/e2e/src/reserve.rs b/e2e/src/reserve.rs new file mode 100644 index 0000000000..217a67578b --- /dev/null +++ b/e2e/src/reserve.rs @@ -0,0 +1,287 @@ +use crate::{ + apiconfig::ApiConfig, + blockchain_api::{CardanoCli, PartnerChainsNode, SubstrateApi}, + conftest::*, +}; +use serde_json::Value as JsonValue; + +const INITIAL_RESERVE_DEPOSIT: i64 = 1000; + +const MIN_LOVELACE_FOR_TX: i64 = 20_000_000; +const MIN_LOVELACE_TO_COVER_FEES: i64 = 10_000_000; + +#[derive(Debug, Clone)] +struct VFunction { + cbor: String, + script_path: String, + script_hash: String, + address: String, + reference_utxo: String, +} + +struct Reserve { + token: String, + v_function: VFunction, +} + +fn reserve_asset_id(config: &ApiConfig, api: &SubstrateApi) -> Result { + let asset_name = config.nodes_config.reserve.as_ref().unwrap().token_name.clone(); + let asset_name_hex = hex::encode(asset_name.as_bytes()); + let policy_id = minting_policy_id(api, config)?; + Ok(format!("{}.{}", policy_id, asset_name_hex)) +} + +fn transaction_input(config: &ApiConfig, api: &SubstrateApi) -> Result, String> { + let utxo_dict = api.cardano_cli().get_utxos(&governance_address(config))?; + Ok(utxo_dict + .iter() + .find(|utxo| utxo.1["value"]["lovelace"].as_i64().expect("i64") > MIN_LOVELACE_FOR_TX) + .map(|x| x.0.clone())) +} + +fn payment_key(config: &ApiConfig) -> String { + config.nodes_config.governance_authority.mainchain_key.clone() +} + +fn cbor_to_bech32(cbor: &str, prefix: &str) -> Result { + let d = &hex::decode(cbor).map_err(|e| e.to_string())?[2..]; + let hrp = bech32::Hrp::parse(prefix).map_err(|e| e.to_string())?; + bech32::encode::(hrp, d).map_err(|e| e.to_string()) +} + +fn hex_to_bech32(hex_string: &str, prefix: &str) -> Result { + let d = hex::decode(hex_string.strip_prefix("0x").unwrap_or(hex_string)) + .map_err(|e| e.to_string())?; + let hrp = bech32::Hrp::parse(prefix).map_err(|e| e.to_string())?; + bech32::encode::(hrp, &d).map_err(|e| e.to_string()) +} + +fn v_function_address(api: &SubstrateApi) -> Result { + let verification_key = api.cardano_cli().generate_payment_keys()?.1; + let bech32_vkey = cbor_to_bech32(verification_key["cborHex"].as_str().unwrap(), "addr_vk")?; + api.cardano_cli().build_address(&bech32_vkey) +} +fn read_v_function_script_file(script_path: String) -> Result { + let script_string = std::fs::read_to_string(script_path).map_err(|e| e.to_string())?; + serde_json::from_str::(&script_string).map_err(|e| e.to_string()) +} +fn attach_v_function_to_utxo( + api: &SubstrateApi, + config: &ApiConfig, + address: &str, + filepath: &str, +) -> Result { + log::info!("Attaching V-function to {address}..."); + let lovelace_amount = MIN_LOVELACE_FOR_TX - MIN_LOVELACE_TO_COVER_FEES; + let raw_tx_filepath = api + .cardano_cli() + .build_tx_with_reference_script( + &transaction_input(config, api)?.expect("utxo exists"), + &address, + lovelace_amount, + &filepath, + &governance_address(config), + )? + .1; + + let signed_tx_filepath = + api.cardano_cli().sign_transaction(&raw_tx_filepath, &payment_key(config))?; + + api.cardano_cli().submit_transaction(&signed_tx_filepath) +} + +fn v_function(api: &SubstrateApi, config: &ApiConfig) -> Result { + let v_function_path = + config.nodes_config.reserve.as_ref().unwrap().v_function_script_path.clone(); + log::info!("Creating V-function from {v_function_path}..."); + let v_function_script = read_v_function_script_file(v_function_path)?; + let v_function_cbor = v_function_script["cborHex"].as_str().expect("string"); + let script_path = write_file( + &api.cardano_cli().run_command, + &v_function_script.to_string().replace("\"", "\\\""), // TODO unacceptable + )?; + let script_hash = api.cardano_cli().get_policy_id(&script_path)?; + let v_function_address = v_function_address(api)?; + attach_v_function_to_utxo(api, config, &v_function_address, &script_path)?; + let reference_utxo = wait_until( + "reference utxo is observable", + || { + let utxo_dict = api.cardano_cli().get_utxos(&v_function_address).ok()?; + utxo_dict + .iter() + .find(|utxo| utxo.1["referenceScript"]["script"]["cborHex"] == v_function_cbor) + .map(|utxo| utxo.0.clone()) + }, + config.timeouts.main_chain_tx, + 3, + )?; + let v_function = VFunction { + cbor: v_function_cbor.to_string(), + script_path, + script_hash, + address: v_function_address, + reference_utxo, + }; + println!("V-function successfully created: {v_function:?}"); + Ok(v_function) +} + +fn reserve( + api: &SubstrateApi, + config: &ApiConfig, + v_function: VFunction, +) -> Result { + Ok(Reserve { token: reserve_asset_id(config, api)?, v_function }) +} + +fn create_reserve( + config: &ApiConfig, + api: &SubstrateApi, + v_function: VFunction, +) -> Result<(), String> { + let reserve = reserve(api, &config, v_function)?; + api.partner_chains_node().reserve_create( + &reserve.v_function.script_hash, + INITIAL_RESERVE_DEPOSIT, + &reserve.token, + &payment_key(config), + )?; + log::info!("Reserve created with initial deposit of {INITIAL_RESERVE_DEPOSIT} tokens"); + Ok(()) +} + +fn clean_up_reserve(config: &ApiConfig, api: &SubstrateApi) -> Result { + log::info!("Cleaning up reserve (handover)..."); + let payment_key = &payment_key(config); + api.partner_chains_node().reserve_handover(payment_key) +} + +fn governance_address(config: &ApiConfig) -> String { + config.nodes_config.governance_authority.mainchain_address.clone() +} + +fn governance_vkey_bech32(config: &ApiConfig) -> Result { + let vkey = &config.nodes_config.governance_authority.mainchain_pub_key.clone().unwrap(); + hex_to_bech32(vkey, "addr_vk") +} + +fn minting_policy_filepath( + api: &SubstrateApi, + config: &ApiConfig, +) -> Result { + let key_hash = api.cardano_cli().get_address_key_hash(&governance_vkey_bech32(config)?)?; + let policy_script = format!(r##"{{\"keyHash\": \"{key_hash}\", \"type\": \"sig\"}}"##); + write_file(&api.cardano_cli().run_command, &policy_script) +} + +fn minting_policy_id(api: &SubstrateApi, config: &ApiConfig) -> Result { + let minting_policy_filepath = &minting_policy_filepath(api, config)?; + api.cardano_cli().get_policy_id(minting_policy_filepath) +} + +fn reserve_initial_balance(api: &SubstrateApi, config: &ApiConfig) -> Result { + api.get_mc_balance( + addresses(api)["ReserveValidator"].as_str().expect("str"), + &reserve_asset_id(&config, api)?, + ) +} + +fn native_token_balance(api: &SubstrateApi, config: &ApiConfig) -> Result { + let balance = + api.get_mc_balance(&governance_address(&config), &reserve_asset_id(&config, api)?)?; + log::info!("Native token balance: {balance}"); + Ok(balance) +} + +fn amount_to_deposit(api: &SubstrateApi, config: &ApiConfig) -> Result { + let reserve_initial_balance = reserve_initial_balance(api, &config)?; + Ok(rand::random_range(1i64..100i64.min(reserve_initial_balance))) +} + +fn deposit_funds( + api: &SubstrateApi, + config: &ApiConfig, + amount_to_deposit: i64, +) -> Result { + api.partner_chains_node() + .reserve_deposit(amount_to_deposit, &payment_key(config)) +} + +fn release_funds( + api: &SubstrateApi, + config: &ApiConfig, + amount_to_release: i64, + reference_utxo: &str, +) -> Result { + api.partner_chains_node().reserve_release( + reference_utxo, + amount_to_release, + &payment_key(config), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deposit_funds() -> Result<(), String> { + let config = ApiConfig::load(); + let api = &SubstrateApi::new(&config); + let payment_key = payment_key(&config); + api.partner_chains_node().reserve_init(&payment_key)?; + let v_function = v_function(api, &config)?; + create_reserve(&config, api, v_function.clone())?; + let amount_to_deposit = amount_to_deposit(api, &config)?; + let initial_balance = reserve_initial_balance(api, &config)?; + let native_token_balance = native_token_balance(api, &config)?; + + // test_deposit_funds + let response = deposit_funds(api, &config, amount_to_deposit); + assert!(response.is_ok()); + + // test_reserve_balance_after_deposit + let reserve_asset_id = &reserve_asset_id(&config, api)?; + let reserve_balance = api.get_mc_balance( + addresses(api)["ReserveValidator"].as_str().expect("str"), + reserve_asset_id, + )?; + assert!(initial_balance + amount_to_deposit == reserve_balance); + + // test_native_token_balance_after_deposit + let native_token = api.get_mc_balance(&governance_address(&config), reserve_asset_id)?; + assert!(native_token_balance - amount_to_deposit == native_token); + clean_up_reserve(&config, api)?; + Ok(()) + } + + #[test] + fn test_release_funds() -> Result<(), String> { + let config = ApiConfig::load(); + let api = &SubstrateApi::new(&config); + let payment_key = payment_key(&config); + api.partner_chains_node().reserve_init(&payment_key)?; + let v_function = v_function(api, &config)?; + create_reserve(&config, api, v_function.clone())?; + let amount_to_release = amount_to_deposit(api, &config)? * 100; // TODO function name + let initial_balance = reserve_initial_balance(api, &config)?; + let native_token_balance = native_token_balance(api, &config)?; + + // test_release_funds + let response = release_funds(api, &config, amount_to_release, &v_function.reference_utxo); + assert!(response.is_ok()); + + // // test_circulation_supply_balance_after_release + // circulation = api.get_mc_balance(addresses["IlliquidCirculationSupplyValidator"], reserve_asset_id) + // assert circulation_supply_initial_balance + amount_to_release == circulation + + // // test_reserve_balance_after_release + // reserve_balance = api.get_mc_balance(addresses["ReserveValidator"], reserve_asset_id) + // assert reserve_initial_balance - amount_to_release == reserve_balance + + // // test_observe_released_funds + // observed_transfer = api.subscribe_token_transfer() + // assert observed_transfer == amount_to_release + Ok(()) + } +} diff --git a/e2e/src/run_command.rs b/e2e/src/run_command.rs new file mode 100644 index 0000000000..b2c0ec9de1 --- /dev/null +++ b/e2e/src/run_command.rs @@ -0,0 +1,42 @@ + +use std::io::Read; + +pub struct Runner { + pub shell: String, +} +impl Runner { + pub fn new(shell: String) -> Self { + Runner { shell } + } + + pub fn run(&self, raw_command: &str, _timeout: u64) -> Result { + use std::process::Stdio; + + let command = format!("{} \"{}\"", self.shell, raw_command.replace("\n", " ")); + log::info!("RUNNING: {command}"); + let mut child = std::process::Command::new("sh") + .arg("-c") + .arg(command) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .map_err(|e| e.to_string())?; + + let mut stderr = String::new(); + std::io::BufReader::new(child.stderr.as_mut().ok_or("can't read stderr")?) + .read_to_string(&mut stderr) + .map_err(|e| e.to_string())?; + + let mut stdout = String::new(); + std::io::BufReader::new(child.stdout.as_mut().ok_or("can't read stdout")?) + .read_to_string(&mut stdout) + .map_err(|e| e.to_string())?; + + let status = child.wait().map_err(|e| e.to_string())?; + if status.success() { + Ok(stdout.trim().to_string()) + } else { + Err(stderr.trim().to_string()) + } + } +}